From 02df00f793b976ba15778fdf28de9a889266e910 Mon Sep 17 00:00:00 2001 From: Bastien Jansen Date: Wed, 28 Oct 2020 17:39:17 +0100 Subject: [PATCH] Add support for JUnit 5 extensions (#57) --- README.md | 22 +- pom.xml | 9 +- .../zapodot/junit/ldap/EmbeddedLdapRule.java | 57 +---- .../junit/ldap/EmbeddedLdapRuleBuilder.java | 233 +---------------- .../junit/ldap/EmbeddedLdapServer.java | 60 +++++ .../internal/AbstractEmbeddedLdapBuilder.java | 240 ++++++++++++++++++ .../internal/EmbeddedLdapExtensionImpl.java | 54 ++++ .../ldap/internal/EmbeddedLdapRuleImpl.java | 141 +--------- .../ldap/internal/EmbeddedLdapServerImpl.java | 148 +++++++++++ .../ldap/junit5/EmbeddedLdapExtension.java | 14 + .../junit5/EmbeddedLdapExtensionBuilder.java | 38 +++ .../EmbeddedLdapExtensionBuilderTest.java | 87 +++++++ .../EmbeddedLdapExtensionStaticTest.java | 28 ++ .../junit5/EmbeddedLdapExtensionTest.java | 94 +++++++ 14 files changed, 799 insertions(+), 426 deletions(-) create mode 100644 src/main/java/org/zapodot/junit/ldap/EmbeddedLdapServer.java create mode 100644 src/main/java/org/zapodot/junit/ldap/internal/AbstractEmbeddedLdapBuilder.java create mode 100644 src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapExtensionImpl.java create mode 100644 src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapServerImpl.java create mode 100644 src/main/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtension.java create mode 100644 src/main/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionBuilder.java create mode 100644 src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionBuilderTest.java create mode 100644 src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionStaticTest.java create mode 100644 src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionTest.java diff --git a/README.md b/README.md index e37a5c2..88517e0 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # embedded-ldap-junit [![Build Status](https://travis-ci.org/zapodot/embedded-ldap-junit.svg?branch=master)](https://travis-ci.org/zapodot/embedded-ldap-junit) [![Coverage Status](https://coveralls.io/repos/zapodot/embedded-ldap-junit/badge.svg)](https://coveralls.io/r/zapodot/embedded-ldap-junit) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.zapodot/embedded-ldap-junit/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.zapodot/embedded-ldap-junit) [![Libraries.io for GitHub](https://img.shields.io/librariesio/github/zapodot/embedded-ldap-junit.svg)](https://libraries.io/github/zapodot/embedded-ldap-junit) [![GitHub](https://img.shields.io/github/license/zapodot/embedded-ldap-junit)](https://github.com/zapodot/embedded-ldap-junit/blob/master/LICENSE) [![Analytics](https://ga-beacon.appspot.com/UA-40926073-2/embedded-ldap-junit/README.md)](https://github.com/igrigorik/ga-beacon) -A [JUnit Rule](//github.com/junit-team/junit/wiki/Rules) for running an embedded LDAP server in your JUnit test based on the wonderful [UnboundID LDAP SDK](https://www.ldap.com/unboundid-ldap-sdk-for-java). Inspired by the [Embedded Database JUnit Rule](//github.com/zapodot/embedded-db-junit). +A [JUnit 4 Rule](//github.com/junit-team/junit/wiki/Rules) and [JUnit 5 Extension](https://junit.org/junit5/docs/current/user-guide/#extensions) for running an embedded LDAP server in your JUnit test based on the wonderful [UnboundID LDAP SDK](https://www.ldap.com/unboundid-ldap-sdk-for-java). Inspired by the [Embedded Database JUnit Rule](//github.com/zapodot/embedded-db-junit). ## Why? * you want to test your LDAP integration code without affecting your LDAP server @@ -46,7 +46,9 @@ Java 8 or higher is required. It has proven pretty useful for several users and libraryDependencies += "org.zapodot" % "embedded-ldap-junit" % "0.8.1" ``` -### Add to Junit test +### Add to JUnit test + +#### JUnit 4 ```java import com.unboundid.ldap.sdk.LDAPInterface; import javax.naming.Context; @@ -96,3 +98,19 @@ public void testContext() throws Exception { assertNotNull(user); } ``` + +#### JUnit 5 +For JUnit 5 tests, simply use `EmbeddedLdapExtensionBuilder` instead of `EmbeddedLdapRuleBuilder`: + +```java +@RegisterExtension +public EmbeddedLdapExtension embeddedLdapRule = EmbeddedLdapExtensionBuilder + .newInstance() + .usingDomainDsn("dc=example,dc=com") + .importingLdifs("example.ldif") + .build(); + +... +``` + +Both JUnit 4 and JUnit 5 builders share the same API, as do the rule and the extension. diff --git a/pom.xml b/pom.xml index 6b05250..6182969 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ 4.13 + 5.7.0 5.0.1 1.7.30 29.0-jre @@ -52,6 +53,12 @@ junit ${junit.version} + + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + true + com.google.guava guava @@ -264,4 +271,4 @@ - \ No newline at end of file + diff --git a/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapRule.java b/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapRule.java index 0b742b4..4aa70d4 100755 --- a/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapRule.java +++ b/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapRule.java @@ -1,65 +1,10 @@ package org.zapodot.junit.ldap; -import com.unboundid.ldap.sdk.LDAPConnection; -import com.unboundid.ldap.sdk.LDAPException; -import com.unboundid.ldap.sdk.LDAPInterface; import org.junit.rules.TestRule; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; - /** * A JUnit rule that may be used as either a @Rule or a @ClassRule */ -public interface EmbeddedLdapRule extends TestRule { - - /** - * For tests depending on the UnboundID LDAP SDK. Returns a proxied version of an Unboundid interface that will be - * closed when the test(s) have been invoked - * - * @return a shared LDAPConnection - * @throws LDAPException if a connection can not be opened - */ - LDAPInterface ldapConnection() throws LDAPException; - - /** - * For tests depending on the UnboundID LDAP SDK that needs access to an ${link LDAPConnection} object - * rather than the interface. If your code does not close the connection for you it will be closed on teardown - * - * @return a LDAPConnection connected to the embedded LDAP server - * @throws LDAPException if an exception occurred while establishing the connection - */ - LDAPConnection unsharedLdapConnection() throws LDAPException; - - /** - * For tests depending on the standard Java JNDI API - * - * @return a shared Context connected to the in-memory LDAP server - * @throws NamingException if context can not be created - */ - Context context() throws NamingException; - - /** - * Like {@link #context()}, but returns a DirContext - * - * @return a DirContext connected to the in-memory LDAP server - * @throws NamingException if a LDAP failure happens during DirContext creation - */ - DirContext dirContext() throws NamingException; - - /** - * Gives access to the listening port for the currently running embedded LDAP server. - * This will make it easier to use other integration mechanisms - *

- * Note: the embedded LDAP server is by default configured to listen only on the loopback address - * (i.e localhost/127.0.0.1) unless another address has been provided to the builder when the rule was built - *

- * - * @return the port number that the embedded server is listening to - * @see org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder#bindingToAddress(String) - */ - int embeddedServerPort(); - +public interface EmbeddedLdapRule extends EmbeddedLdapServer, TestRule { } diff --git a/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapRuleBuilder.java b/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapRuleBuilder.java index 05ef238..b00771e 100755 --- a/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapRuleBuilder.java +++ b/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapRuleBuilder.java @@ -1,60 +1,14 @@ package org.zapodot.junit.ldap; -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.collect.Lists; -import com.google.common.io.Resources; -import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; -import com.unboundid.ldap.listener.InMemoryListenerConfig; -import com.unboundid.ldap.sdk.LDAPException; -import com.unboundid.ldap.sdk.schema.Schema; -import com.unboundid.ldif.LDIFException; -import org.zapodot.junit.ldap.internal.AuthenticationConfiguration; +import org.zapodot.junit.ldap.internal.AbstractEmbeddedLdapBuilder; import org.zapodot.junit.ldap.internal.EmbeddedLdapRuleImpl; -import java.io.File; -import java.io.IOException; -import java.net.InetAddress; -import java.net.URISyntaxException; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; import java.util.Objects; /** * A builder providing a fluent way of defining EmbeddedLdapRule instances */ -public class EmbeddedLdapRuleBuilder { - - public static final String DEFAULT_DOMAIN = "dc=example,dc=com"; - public static final String DEFAULT_BIND_DSN = "cn=Directory manager"; - public static final String DEFAULT_BIND_CREDENTIALS = "password"; - public static final String LDAP_SERVER_LISTENER_NAME = "test-listener"; - public static final int MIN_PORT_EXCLUSIVE = 0; - public static final int MAX_PORT_EXCLUSIVE = 65535; - private List domainDsn = new LinkedList<>(); - - private String bindDSN = DEFAULT_BIND_DSN; - - private String bindCredentials = DEFAULT_BIND_CREDENTIALS; - - private List ldifsToImport = new LinkedList<>(); - - private List schemaLdifs = new LinkedList<>(); - - private boolean addDefaultSchema = true; - - private Integer bindPort = 0; - - private InetAddress bindAddress = InetAddress.getLoopbackAddress(); - - private AuthenticationConfiguration authenticationConfiguration; - - private InMemoryListenerConfig listenerConfig = null; - - public EmbeddedLdapRuleBuilder() { - } +public class EmbeddedLdapRuleBuilder extends AbstractEmbeddedLdapBuilder { /** * Creates a new builder @@ -65,109 +19,8 @@ public static EmbeddedLdapRuleBuilder newInstance() { return new EmbeddedLdapRuleBuilder(); } - /** - * Sets a domainDsn to be used. May be multiple values. If not set, it will default to the value of the {@link #DEFAULT_DOMAIN DEFAULT_DOMAIN} field - * - * @param domainDsn a valid DSN string - * @return same EmbeddedLdapRuleBuilder instance with the domainDsn field set - */ - public EmbeddedLdapRuleBuilder usingDomainDsn(final String domainDsn) { - this.domainDsn.add(domainDsn); - return this; - } - - /** - * Sets the DSN to bind to when authenticating. If not set, it will default to the value of the {@link #DEFAULT_BIND_DSN DEFAULT_BIND_DSN} field - * - * @param bindDSN a valid DSN string - * @return same EmbeddedLdapRuleBuilder instance with the bindDSN field set - */ - public EmbeddedLdapRuleBuilder usingBindDSN(final String bindDSN) { - this.bindDSN = bindDSN; - return this; - } - - /** - * Sets the credentials to be used to authenticate. If not set, it will default to the value of the {@link #DEFAULT_BIND_CREDENTIALS DEFAULT_BIND_CREDENTIALS} field - * - * @param bindCredentials a password string - * @return same EmbeddedLdapRuleBuilder instance with the bindCredentials field set - */ - public EmbeddedLdapRuleBuilder usingBindCredentials(final String bindCredentials) { - this.bindCredentials = bindCredentials; - return this; - } - - /** - * Sets the port that the in-memory LDAP server will bind to. If not set, an available port will be picked automatically - * - * @param port a port number - * @return same EmbeddedLdapRuleBuilder instance with the port field set - * @throws IllegalArgumentException if the provided value for port is not between @{link MIN_PORT_EXCLUSIVE} - * and @{MAX_PORT_EXCLUSIVE} (exclusive) - */ - public EmbeddedLdapRuleBuilder bindingToPort(final int port) { - if ((port < MIN_PORT_EXCLUSIVE) || (port > MAX_PORT_EXCLUSIVE)) { - throw new IllegalArgumentException(String.format("Value \"%s\" is not a valid port number", port)); - } - this.bindPort = Integer.valueOf(port); - return this; - } - - /** - * Allows the listening address for the embedded LDAP server to be set. If not set it will bind to localhost/127.0.0.1. - * - * @param address a valid hostname or textual representation of an IP address - * @return same EmbeddedLdapRuleBuilder instance with the bindAddress field set - * @throws IllegalArgumentException if the value provided for \"address\" is invalid - */ - public EmbeddedLdapRuleBuilder bindingToAddress(final String address) { - Objects.requireNonNull(address); - try { - final InetAddress addressByName = InetAddress.getByName(address); - this.bindAddress = addressByName; - } catch (UnknownHostException e) { - throw new IllegalArgumentException(String.format("Unknown host address \"%s\"", address), e); - } - return this; - } - - /** - * Avoid adding UnboundID's default schema that contains the most common LDAP elements defined through various RFC's. - * - * @return same EmbeddedLdapRuleBuilder instance with the withoutDefaultSchema field set to FALSE - */ - public EmbeddedLdapRuleBuilder withoutDefaultSchema() { - this.addDefaultSchema = false; - return this; - } - - /** - * Define schemas to be used for the server. If not defined, UnboundID will set up a default schema. - * - * @param ldifSchemaFiles LDIF-files containing schema element definitions - * @return same EmbeddedLdapRuleBuilder with the given LDIF-files added to the internal schema file collection. - */ - public EmbeddedLdapRuleBuilder withSchema(final String... ldifSchemaFiles) { - this.schemaLdifs.addAll(Arrays.asList(ldifSchemaFiles)); - return this; - } - - /** - * Specify one or more LDIF resources to be imported on startup. - * - * @param ldifFiles LDIF-files to import - * @return same EmbeddedLdapRuleBuilder instance with the provided ldifFiles added to the list of LDIF files to import - */ - public EmbeddedLdapRuleBuilder importingLdifs(final String... ldifFiles) { - if (ldifFiles != null) { - ldifsToImport.addAll(Arrays.asList(ldifFiles)); - } - return this; - } - - public EmbeddedLdapRuleBuilder withListener(InMemoryListenerConfig listenerConfig) { - this.listenerConfig = listenerConfig; + @Override + protected EmbeddedLdapRuleBuilder getThis() { return this; } @@ -183,82 +36,4 @@ public EmbeddedLdapRule build() { ldifsToImport); } - private InMemoryDirectoryServerConfig createInMemoryServerConfiguration() { - try { - final InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig = - new InMemoryDirectoryServerConfig(domainDsnArray()); - - if (bindCredentials != null) { - this.authenticationConfiguration = new AuthenticationConfiguration(bindDSN, bindCredentials); - inMemoryDirectoryServerConfig.addAdditionalBindCredentials(bindDSN, bindCredentials); - } - - if (listenerConfig == null) { - listenerConfig = InMemoryListenerConfig.createLDAPConfig( - LDAP_SERVER_LISTENER_NAME, - bindAddress, - bindPort, - null); - } - inMemoryDirectoryServerConfig.setListenerConfigs(listenerConfig); - inMemoryDirectoryServerConfig.setSchema(customSchema()); - return inMemoryDirectoryServerConfig; - } catch (LDAPException e) { - throw new IllegalStateException( - "Could not create configuration for the in-memory LDAP instance due to an exception", - e); - } - } - - private String[] domainDsnArray() { - if (domainDsn.size() == 0) { - return new String[]{DEFAULT_DOMAIN}; - } else { - return domainDsn.toArray(new String[]{}); - } - } - - private Schema customSchema() { - final List schemaFiles = schemaFiles(); - - try { - final Schema initialSchema = (addDefaultSchema ? Schema.getDefaultStandardSchema() : null); - if (!schemaFiles.isEmpty()) { - final Schema customSchema = initialSchema == null - ? Schema.getSchema(schemaFiles) - : Schema.mergeSchemas(initialSchema, Schema.getSchema(schemaFiles)); - return customSchema; - } else { - return null; - } - - } catch (IOException | LDIFException | LDAPException e) { - throw new IllegalArgumentException( - "Could not create custom LDAP schema due, probably caused by an incorrectly formatted schema", - e); - } - } - - private List schemaFiles() { - return Lists.newArrayList(Lists.transform(this.schemaLdifs, new Function() { - @Override - public File apply(final String input) { - try { - final File file = new File(Resources.getResource(input).toURI()); - if (!file.isFile()) { - throw new IllegalArgumentException(String.format( - "The resource named \"%s\" can not be found or is not a file", - input)); - } - return file; - } catch (URISyntaxException e) { - throw new IllegalArgumentException(String.format( - "The resource named \"%s\" is not a valid file reference", - input), e); - } - } - })); - } - - } diff --git a/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapServer.java b/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapServer.java new file mode 100644 index 0000000..45191d9 --- /dev/null +++ b/src/main/java/org/zapodot/junit/ldap/EmbeddedLdapServer.java @@ -0,0 +1,60 @@ +package org.zapodot.junit.ldap; + +import com.unboundid.ldap.sdk.LDAPConnection; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldap.sdk.LDAPInterface; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; + +public interface EmbeddedLdapServer { + + /** + * For tests depending on the UnboundID LDAP SDK. Returns a proxied version of an Unboundid interface that will be + * closed when the test(s) have been invoked + * + * @return a shared LDAPConnection + * @throws LDAPException if a connection can not be opened + */ + LDAPInterface ldapConnection() throws LDAPException; + + /** + * For tests depending on the UnboundID LDAP SDK that needs access to an ${link LDAPConnection} object + * rather than the interface. If your code does not close the connection for you it will be closed on teardown + * + * @return a LDAPConnection connected to the embedded LDAP server + * @throws LDAPException if an exception occurred while establishing the connection + */ + LDAPConnection unsharedLdapConnection() throws LDAPException; + + /** + * For tests depending on the standard Java JNDI API + * + * @return a shared Context connected to the in-memory LDAP server + * @throws NamingException if context can not be created + */ + Context context() throws NamingException; + + /** + * Like {@link #context()}, but returns a DirContext + * + * @return a DirContext connected to the in-memory LDAP server + * @throws NamingException if a LDAP failure happens during DirContext creation + */ + DirContext dirContext() throws NamingException; + + /** + * Gives access to the listening port for the currently running embedded LDAP server. + * This will make it easier to use other integration mechanisms + *

+ * Note: the embedded LDAP server is by default configured to listen only on the loopback address + * (i.e localhost/127.0.0.1) unless another address has been provided to the builder when the rule was built + *

+ * + * @return the port number that the embedded server is listening to + * @see org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder#bindingToAddress(String) + */ + int embeddedServerPort(); + +} diff --git a/src/main/java/org/zapodot/junit/ldap/internal/AbstractEmbeddedLdapBuilder.java b/src/main/java/org/zapodot/junit/ldap/internal/AbstractEmbeddedLdapBuilder.java new file mode 100644 index 0000000..e5326ea --- /dev/null +++ b/src/main/java/org/zapodot/junit/ldap/internal/AbstractEmbeddedLdapBuilder.java @@ -0,0 +1,240 @@ +package org.zapodot.junit.ldap.internal; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import com.google.common.io.Resources; +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; +import com.unboundid.ldap.listener.InMemoryListenerConfig; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldap.sdk.schema.Schema; +import com.unboundid.ldif.LDIFException; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * A builder providing a fluent way of defining {@link org.zapodot.junit.ldap.EmbeddedLdapRule} + * or {@link org.zapodot.junit.ldap.junit5.EmbeddedLdapExtension} instances. + */ +public abstract class AbstractEmbeddedLdapBuilder> { + + public static final String DEFAULT_DOMAIN = "dc=example,dc=com"; + public static final String DEFAULT_BIND_DSN = "cn=Directory manager"; + public static final String DEFAULT_BIND_CREDENTIALS = "password"; + public static final String LDAP_SERVER_LISTENER_NAME = "test-listener"; + public static final int MIN_PORT_EXCLUSIVE = 0; + public static final int MAX_PORT_EXCLUSIVE = 65535; + private List domainDsn = new LinkedList<>(); + + protected String bindDSN = DEFAULT_BIND_DSN; + + private String bindCredentials = DEFAULT_BIND_CREDENTIALS; + + protected List ldifsToImport = new LinkedList<>(); + + private List schemaLdifs = new LinkedList<>(); + + private boolean addDefaultSchema = true; + + private Integer bindPort = 0; + + private InetAddress bindAddress = InetAddress.getLoopbackAddress(); + + protected AuthenticationConfiguration authenticationConfiguration; + + private InMemoryListenerConfig listenerConfig = null; + + /** + * Sets a domainDsn to be used. May be multiple values. If not set, it will default to the value of the {@link #DEFAULT_DOMAIN DEFAULT_DOMAIN} field + * + * @param domainDsn a valid DSN string + * @return same builder instance with the domainDsn field set + */ + public Self usingDomainDsn(final String domainDsn) { + this.domainDsn.add(domainDsn); + return getThis(); + } + + /** + * Sets the DSN to bind to when authenticating. If not set, it will default to the value of the {@link #DEFAULT_BIND_DSN DEFAULT_BIND_DSN} field + * + * @param bindDSN a valid DSN string + * @return same builder instance with the bindDSN field set + */ + public Self usingBindDSN(final String bindDSN) { + this.bindDSN = bindDSN; + return getThis(); + } + + /** + * Sets the credentials to be used to authenticate. If not set, it will default to the value of the {@link #DEFAULT_BIND_CREDENTIALS DEFAULT_BIND_CREDENTIALS} field + * + * @param bindCredentials a password string + * @return same builder instance with the bindCredentials field set + */ + public Self usingBindCredentials(final String bindCredentials) { + this.bindCredentials = bindCredentials; + return getThis(); + } + + /** + * Sets the port that the in-memory LDAP server will bind to. If not set, an available port will be picked automatically + * + * @param port a port number + * @return same builder instance with the port field set + * @throws IllegalArgumentException if the provided value for port is not between @{link MIN_PORT_EXCLUSIVE} + * and @{MAX_PORT_EXCLUSIVE} (exclusive) + */ + public Self bindingToPort(final int port) { + if ((port < MIN_PORT_EXCLUSIVE) || (port > MAX_PORT_EXCLUSIVE)) { + throw new IllegalArgumentException(String.format("Value \"%s\" is not a valid port number", port)); + } + this.bindPort = Integer.valueOf(port); + return getThis(); + } + + /** + * Allows the listening address for the embedded LDAP server to be set. If not set it will bind to localhost/127.0.0.1. + * + * @param address a valid hostname or textual representation of an IP address + * @return same builder instance with the bindAddress field set + * @throws IllegalArgumentException if the value provided for \"address\" is invalid + */ + public Self bindingToAddress(final String address) { + Objects.requireNonNull(address); + try { + final InetAddress addressByName = InetAddress.getByName(address); + this.bindAddress = addressByName; + } catch (UnknownHostException e) { + throw new IllegalArgumentException(String.format("Unknown host address \"%s\"", address), e); + } + return getThis(); + } + + /** + * Avoid adding UnboundID's default schema that contains the most common LDAP elements defined through various RFC's. + * + * @return same builder instance with the withoutDefaultSchema field set to FALSE + */ + public Self withoutDefaultSchema() { + this.addDefaultSchema = false; + return getThis(); + } + + /** + * Define schemas to be used for the server. If not defined, UnboundID will set up a default schema. + * + * @param ldifSchemaFiles LDIF-files containing schema element definitions + * @return same builder with the given LDIF-files added to the internal schema file collection. + */ + public Self withSchema(final String... ldifSchemaFiles) { + this.schemaLdifs.addAll(Arrays.asList(ldifSchemaFiles)); + return getThis(); + } + + /** + * Specify one or more LDIF resources to be imported on startup. + * + * @param ldifFiles LDIF-files to import + * @return same builder instance with the provided ldifFiles added to the list of LDIF files to import + */ + public Self importingLdifs(final String... ldifFiles) { + if (ldifFiles != null) { + ldifsToImport.addAll(Arrays.asList(ldifFiles)); + } + return getThis(); + } + + public Self withListener(InMemoryListenerConfig listenerConfig) { + this.listenerConfig = listenerConfig; + return getThis(); + } + + protected abstract Self getThis(); + + protected InMemoryDirectoryServerConfig createInMemoryServerConfiguration() { + try { + final InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig = + new InMemoryDirectoryServerConfig(domainDsnArray()); + + if (bindCredentials != null) { + this.authenticationConfiguration = new AuthenticationConfiguration(bindDSN, bindCredentials); + inMemoryDirectoryServerConfig.addAdditionalBindCredentials(bindDSN, bindCredentials); + } + + if (listenerConfig == null) { + listenerConfig = InMemoryListenerConfig.createLDAPConfig( + LDAP_SERVER_LISTENER_NAME, + bindAddress, + bindPort, + null); + } + inMemoryDirectoryServerConfig.setListenerConfigs(listenerConfig); + inMemoryDirectoryServerConfig.setSchema(customSchema()); + return inMemoryDirectoryServerConfig; + } catch (LDAPException e) { + throw new IllegalStateException( + "Could not create configuration for the in-memory LDAP instance due to an exception", + e); + } + } + + private String[] domainDsnArray() { + if (domainDsn.size() == 0) { + return new String[]{DEFAULT_DOMAIN}; + } else { + return domainDsn.toArray(new String[]{}); + } + } + + private Schema customSchema() { + final List schemaFiles = schemaFiles(); + + try { + final Schema initialSchema = (addDefaultSchema ? Schema.getDefaultStandardSchema() : null); + if (!schemaFiles.isEmpty()) { + final Schema customSchema = initialSchema == null + ? Schema.getSchema(schemaFiles) + : Schema.mergeSchemas(initialSchema, Schema.getSchema(schemaFiles)); + return customSchema; + } else { + return null; + } + + } catch (IOException | LDIFException | LDAPException e) { + throw new IllegalArgumentException( + "Could not create custom LDAP schema due, probably caused by an incorrectly formatted schema", + e); + } + } + + private List schemaFiles() { + return Lists.newArrayList(Lists.transform(this.schemaLdifs, new Function() { + @Override + public File apply(final String input) { + try { + final File file = new File(Resources.getResource(input).toURI()); + if (!file.isFile()) { + throw new IllegalArgumentException(String.format( + "The resource named \"%s\" can not be found or is not a file", + input)); + } + return file; + } catch (URISyntaxException e) { + throw new IllegalArgumentException(String.format( + "The resource named \"%s\" is not a valid file reference", + input), e); + } + } + })); + } + + +} diff --git a/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapExtensionImpl.java b/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapExtensionImpl.java new file mode 100644 index 0000000..1daf1ab --- /dev/null +++ b/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapExtensionImpl.java @@ -0,0 +1,54 @@ +package org.zapodot.junit.ldap.internal; + +import com.unboundid.ldap.listener.InMemoryDirectoryServer; +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; +import com.unboundid.ldap.sdk.LDAPException; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.zapodot.junit.ldap.junit5.EmbeddedLdapExtension; + +import java.util.List; + +public class EmbeddedLdapExtensionImpl extends EmbeddedLdapServerImpl implements EmbeddedLdapExtension { + + public static EmbeddedLdapExtension createForConfiguration(final InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig, + final AuthenticationConfiguration authenticationConfiguration, + final List ldifs) { + try { + return new EmbeddedLdapExtensionImpl(createServer(inMemoryDirectoryServerConfig, ldifs), + authenticationConfiguration); + } catch (LDAPException e) { + throw new IllegalStateException("Can not initiate in-memory LDAP server due to an exception", e); + } + } + + private boolean isStartedBeforeAll = false; + + public EmbeddedLdapExtensionImpl(InMemoryDirectoryServer inMemoryDirectoryServer, AuthenticationConfiguration authenticationConfiguration1) { + super(inMemoryDirectoryServer, authenticationConfiguration1); + } + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + isStartedBeforeAll = true; + startEmbeddedLdapServer(); + } + + @Override + public void afterAll(ExtensionContext extensionContext) { + takeDownEmbeddedLdapServer(); + } + + @Override + public void beforeEach(ExtensionContext extensionContext) throws Exception { + if (!isStartedBeforeAll) { + startEmbeddedLdapServer(); + } + } + + @Override + public void afterEach(ExtensionContext extensionContext) { + if (!isStartedBeforeAll) { + takeDownEmbeddedLdapServer(); + } + } +} diff --git a/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapRuleImpl.java b/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapRuleImpl.java index baaca0c..9f33d28 100755 --- a/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapRuleImpl.java +++ b/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapRuleImpl.java @@ -1,49 +1,15 @@ package org.zapodot.junit.ldap.internal; -import com.google.common.base.Charsets; -import com.google.common.io.Resources; import com.unboundid.ldap.listener.InMemoryDirectoryServer; import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; -import com.unboundid.ldap.sdk.LDAPConnection; import com.unboundid.ldap.sdk.LDAPException; -import com.unboundid.ldap.sdk.LDAPInterface; import org.junit.runner.Description; import org.junit.runners.model.Statement; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.zapodot.junit.ldap.EmbeddedLdapRule; -import org.zapodot.junit.ldap.internal.jndi.ContextProxyFactory; -import org.zapodot.junit.ldap.internal.unboundid.LDAPInterfaceProxyFactory; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; -import javax.naming.ldap.LdapContext; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.Hashtable; import java.util.List; -public class EmbeddedLdapRuleImpl implements EmbeddedLdapRule { - - private static final String JAVA_RT_CONTROL_FACTORY = "com.sun.jndi.ldap.DefaultResponseControlFactory"; - - private static final String JAVA_RT_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory"; - - private static Logger logger = LoggerFactory.getLogger(EmbeddedLdapRuleImpl.class); - private final InMemoryDirectoryServer inMemoryDirectoryServer; - private final AuthenticationConfiguration authenticationConfiguration; - private LDAPConnection ldapConnection; - private InitialDirContext initialDirContext; - private boolean isStarted = false; - - - private EmbeddedLdapRuleImpl(final InMemoryDirectoryServer inMemoryDirectoryServer, - final AuthenticationConfiguration authenticationConfiguration1) { - this.inMemoryDirectoryServer = inMemoryDirectoryServer; - this.authenticationConfiguration = authenticationConfiguration1; - } +public class EmbeddedLdapRuleImpl extends EmbeddedLdapServerImpl implements EmbeddedLdapRule { public static EmbeddedLdapRule createForConfiguration(final InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig, final AuthenticationConfiguration authenticationConfiguration, @@ -56,89 +22,10 @@ public static EmbeddedLdapRule createForConfiguration(final InMemoryDirectorySer } } - private static InMemoryDirectoryServer createServer(final InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig, - final List ldifs) throws LDAPException { - final InMemoryDirectoryServer ldapServer = - new InMemoryDirectoryServer(inMemoryDirectoryServerConfig); - if (ldifs != null && !ldifs.isEmpty()) { - for (final String ldif : ldifs) { - try { - ldapServer.importFromLDIF(false, URLDecoder.decode(Resources.getResource(ldif).getPath(), - Charsets.UTF_8.name())); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("Can not URL decode path:" + Resources.getResource(ldif).getPath(), - e); - } - } - } - return ldapServer; - } - - @Override - public LDAPInterface ldapConnection() throws LDAPException { - return LDAPInterfaceProxyFactory.createProxy(createOrGetLdapConnection()); - } - - @Override - public LDAPConnection unsharedLdapConnection() throws LDAPException { - return createOrGetLdapConnection(); - } - - private LDAPConnection createOrGetLdapConnection() throws LDAPException { - if (isStarted) { - if (ldapConnection == null || ! ldapConnection.isConnected()) { - ldapConnection = inMemoryDirectoryServer.getConnection(); - } - return ldapConnection; - } else { - throw new IllegalStateException( - "Can not get a LdapConnection before the embedded LDAP server has been started"); - } - } - - @Override - public Context context() throws NamingException { - return ContextProxyFactory.asDelegatingContext(createOrGetInitialDirContext()); - } - - @Override - public DirContext dirContext() throws NamingException { - return ContextProxyFactory.asDelegatingDirContext(createOrGetInitialDirContext()); - } - - @Override - public int embeddedServerPort() { - if(isStarted) { - return inMemoryDirectoryServer.getListenPort(); - } else { - throw new IllegalStateException("The embedded server must be started prior to accessing the listening port"); - } - } - - private InitialDirContext createOrGetInitialDirContext() throws NamingException { - if (isStarted) { - if (initialDirContext == null) { - initialDirContext = new InitialDirContext(createLdapEnvironment()); - } - return initialDirContext; - } else { - throw new IllegalStateException( - "Can not get an InitialDirContext before the embedded LDAP server has been started"); - } + public EmbeddedLdapRuleImpl(InMemoryDirectoryServer inMemoryDirectoryServer, AuthenticationConfiguration authenticationConfiguration1) { + super(inMemoryDirectoryServer, authenticationConfiguration1); } - private Hashtable createLdapEnvironment() { - final Hashtable environment = new Hashtable<>(); - environment.put(LdapContext.CONTROL_FACTORIES, JAVA_RT_CONTROL_FACTORY); - environment.put(Context.PROVIDER_URL, String.format("ldap://%s:%s", - inMemoryDirectoryServer.getListenAddress().getHostName(), - embeddedServerPort())); - environment.put(Context.INITIAL_CONTEXT_FACTORY, JAVA_RT_CONTEXT_FACTORY); - if (authenticationConfiguration != null) { - environment.putAll(authenticationConfiguration.toAuthenticationEnvironment()); - } - return environment; - } @Override public Statement apply(final Statement base, final Description description) { @@ -159,26 +46,4 @@ public void evaluate() throws Throwable { }; } - private void startEmbeddedLdapServer() throws LDAPException { - inMemoryDirectoryServer.startListening(); - isStarted = true; - } - - private void takeDownEmbeddedLdapServer() { - try { - if (ldapConnection != null && ldapConnection.isConnected()) { - ldapConnection.close(); - } - if (initialDirContext != null) { - initialDirContext.close(); - } - } catch (NamingException e) { - logger.info("Could not close initial context, forcing server shutdown anyway", e); - } finally { - inMemoryDirectoryServer.shutDown(true); - } - - } - - } diff --git a/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapServerImpl.java b/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapServerImpl.java new file mode 100644 index 0000000..161ee12 --- /dev/null +++ b/src/main/java/org/zapodot/junit/ldap/internal/EmbeddedLdapServerImpl.java @@ -0,0 +1,148 @@ +package org.zapodot.junit.ldap.internal; + +import com.google.common.base.Charsets; +import com.google.common.io.Resources; +import com.unboundid.ldap.listener.InMemoryDirectoryServer; +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; +import com.unboundid.ldap.sdk.LDAPConnection; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldap.sdk.LDAPInterface; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zapodot.junit.ldap.EmbeddedLdapServer; +import org.zapodot.junit.ldap.internal.jndi.ContextProxyFactory; +import org.zapodot.junit.ldap.internal.unboundid.LDAPInterfaceProxyFactory; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.ldap.LdapContext; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Hashtable; +import java.util.List; + +abstract class EmbeddedLdapServerImpl implements EmbeddedLdapServer { + private static final String JAVA_RT_CONTROL_FACTORY = "com.sun.jndi.ldap.DefaultResponseControlFactory"; + + private static final String JAVA_RT_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory"; + + private static Logger logger = LoggerFactory.getLogger(EmbeddedLdapExtensionImpl.class); + private final InMemoryDirectoryServer inMemoryDirectoryServer; + private final AuthenticationConfiguration authenticationConfiguration; + private LDAPConnection ldapConnection; + private InitialDirContext initialDirContext; + private boolean isStarted = false; + + public EmbeddedLdapServerImpl(final InMemoryDirectoryServer inMemoryDirectoryServer, + final AuthenticationConfiguration authenticationConfiguration1) { + this.inMemoryDirectoryServer = inMemoryDirectoryServer; + this.authenticationConfiguration = authenticationConfiguration1; + } + + protected static InMemoryDirectoryServer createServer(final InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig, + final List ldifs) throws LDAPException { + final InMemoryDirectoryServer ldapServer = + new InMemoryDirectoryServer(inMemoryDirectoryServerConfig); + if (ldifs != null && !ldifs.isEmpty()) { + for (final String ldif : ldifs) { + try { + ldapServer.importFromLDIF(false, URLDecoder.decode(Resources.getResource(ldif).getPath(), + Charsets.UTF_8.name())); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("Can not URL decode path:" + Resources.getResource(ldif).getPath(), + e); + } + } + } + return ldapServer; + } + + @Override + public LDAPInterface ldapConnection() throws LDAPException { + return LDAPInterfaceProxyFactory.createProxy(createOrGetLdapConnection()); + } + + @Override + public LDAPConnection unsharedLdapConnection() throws LDAPException { + return createOrGetLdapConnection(); + } + + private LDAPConnection createOrGetLdapConnection() throws LDAPException { + if (isStarted) { + if (ldapConnection == null || ! ldapConnection.isConnected()) { + ldapConnection = inMemoryDirectoryServer.getConnection(); + } + return ldapConnection; + } else { + throw new IllegalStateException( + "Can not get a LdapConnection before the embedded LDAP server has been started"); + } + } + + @Override + public Context context() throws NamingException { + return ContextProxyFactory.asDelegatingContext(createOrGetInitialDirContext()); + } + + @Override + public DirContext dirContext() throws NamingException { + return ContextProxyFactory.asDelegatingDirContext(createOrGetInitialDirContext()); + } + + @Override + public int embeddedServerPort() { + if(isStarted) { + return inMemoryDirectoryServer.getListenPort(); + } else { + throw new IllegalStateException("The embedded server must be started prior to accessing the listening port"); + } + } + + private InitialDirContext createOrGetInitialDirContext() throws NamingException { + if (isStarted) { + if (initialDirContext == null) { + initialDirContext = new InitialDirContext(createLdapEnvironment()); + } + return initialDirContext; + } else { + throw new IllegalStateException( + "Can not get an InitialDirContext before the embedded LDAP server has been started"); + } + } + + private Hashtable createLdapEnvironment() { + final Hashtable environment = new Hashtable<>(); + environment.put(LdapContext.CONTROL_FACTORIES, JAVA_RT_CONTROL_FACTORY); + environment.put(Context.PROVIDER_URL, String.format("ldap://%s:%s", + inMemoryDirectoryServer.getListenAddress().getHostName(), + embeddedServerPort())); + environment.put(Context.INITIAL_CONTEXT_FACTORY, JAVA_RT_CONTEXT_FACTORY); + if (authenticationConfiguration != null) { + environment.putAll(authenticationConfiguration.toAuthenticationEnvironment()); + } + return environment; + } + + protected void startEmbeddedLdapServer() throws LDAPException { + inMemoryDirectoryServer.startListening(); + isStarted = true; + } + + protected void takeDownEmbeddedLdapServer() { + try { + if (ldapConnection != null && ldapConnection.isConnected()) { + ldapConnection.close(); + } + if (initialDirContext != null) { + initialDirContext.close(); + } + } catch (NamingException e) { + logger.info("Could not close initial context, forcing server shutdown anyway", e); + } finally { + inMemoryDirectoryServer.shutDown(true); + } + + } +} diff --git a/src/main/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtension.java b/src/main/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtension.java new file mode 100644 index 0000000..10ccbf3 --- /dev/null +++ b/src/main/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtension.java @@ -0,0 +1,14 @@ +package org.zapodot.junit.ldap.junit5; + +import org.junit.jupiter.api.extension.*; +import org.zapodot.junit.ldap.EmbeddedLdapServer; + +/** + * A JUnit 5 extension that can be registered with @{@link RegisterExtension} + * (supports both {@code static} and instance fields). + */ +public interface EmbeddedLdapExtension extends EmbeddedLdapServer, Extension, + BeforeEachCallback, AfterEachCallback, + BeforeAllCallback, AfterAllCallback { + +} diff --git a/src/main/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionBuilder.java b/src/main/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionBuilder.java new file mode 100644 index 0000000..3bee8b7 --- /dev/null +++ b/src/main/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionBuilder.java @@ -0,0 +1,38 @@ +package org.zapodot.junit.ldap.junit5; + +import org.zapodot.junit.ldap.internal.AbstractEmbeddedLdapBuilder; +import org.zapodot.junit.ldap.internal.EmbeddedLdapExtensionImpl; + +import java.util.Objects; + +/** + * A builder providing a fluent way of defining {@link EmbeddedLdapExtension} instances. + */ +public class EmbeddedLdapExtensionBuilder extends AbstractEmbeddedLdapBuilder { + + /** + * Creates a new builder + * + * @return a new EmbeddedLdapExtensionBuilder instance + */ + public static EmbeddedLdapExtensionBuilder newInstance() { + return new EmbeddedLdapExtensionBuilder(); + } + + /** + * Creates a new extension based on the information that was previously provided + * + * @return a new EmbeddedLdapExtension instance + */ + public EmbeddedLdapExtension build() { + Objects.requireNonNull(bindDSN, "\"bindDSN\" can not be null"); + return EmbeddedLdapExtensionImpl.createForConfiguration(createInMemoryServerConfiguration(), + authenticationConfiguration, + ldifsToImport); + } + + @Override + protected EmbeddedLdapExtensionBuilder getThis() { + return this; + } +} diff --git a/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionBuilderTest.java b/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionBuilderTest.java new file mode 100644 index 0000000..77d0d1e --- /dev/null +++ b/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionBuilderTest.java @@ -0,0 +1,87 @@ +package org.zapodot.junit.ldap.junit5; + +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class EmbeddedLdapExtensionBuilderTest { + + @Test + public void bindingToLegalPort() { + assertNotNull(EmbeddedLdapExtensionBuilder.newInstance().bindingToPort(9999)); + } + + @Test(expected = IllegalStateException.class) + public void testPrematureLdapConnection() throws Exception { + EmbeddedLdapExtensionBuilder.newInstance().build().ldapConnection(); + + } + + @Test(expected = IllegalStateException.class) + public void testPrematureContext() throws Exception { + EmbeddedLdapExtensionBuilder.newInstance().build().context(); + + } + + @Test(expected = IllegalArgumentException.class) + public void testUnknownLDIF() { + EmbeddedLdapExtensionBuilder.newInstance().importingLdifs("nonExisting.ldif").build(); + + } + + @Test + public void testNullLDIF() { + assertNotNull(EmbeddedLdapExtensionBuilder.newInstance().importingLdifs(null).build()); + + } + + @Test(expected = IllegalStateException.class) + public void testIllegalDSN() { + EmbeddedLdapExtensionBuilder.newInstance().usingBindDSN("bindDsn").build(); + + } + + @Test(expected = IllegalArgumentException.class) + public void testIllegalPort() { + EmbeddedLdapExtensionBuilder.newInstance().bindingToPort(Integer.MIN_VALUE).build(); + + } + + @Test(expected = IllegalArgumentException.class) + public void testSchemaNotFound() { + EmbeddedLdapExtensionBuilder.newInstance().withSchema("non-existing-schema.ldif").build(); + + } + + @Test(expected = IllegalArgumentException.class) + public void testSchemaIsNotAFile() { + EmbeddedLdapExtensionBuilder.newInstance().withSchema("folder").build(); + + } + + @Test(expected = IllegalArgumentException.class) + public void testSchemaIsInvalid() { + EmbeddedLdapExtensionBuilder.newInstance().withSchema("invalid.ldif").build(); + + } + + @Test(expected = IllegalArgumentException.class) + public void testSchemaFileUnsupportedIsInvalid() { + EmbeddedLdapExtensionBuilder.newInstance().withSchema("\"#%¤&&%/¤##¤¤").build(); + + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidPort() { + EmbeddedLdapExtensionBuilder.newInstance().bindingToPort(Integer.MAX_VALUE); + + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidBindAddress() { + EmbeddedLdapExtensionBuilder.newInstance().bindingToAddress("åpsldfåpl"); + + } + + +} diff --git a/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionStaticTest.java b/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionStaticTest.java new file mode 100644 index 0000000..5fa054f --- /dev/null +++ b/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionStaticTest.java @@ -0,0 +1,28 @@ +package org.zapodot.junit.ldap.junit5; + +import com.unboundid.ldap.sdk.LDAPInterface; +import com.unboundid.ldap.sdk.SearchScope; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class EmbeddedLdapExtensionStaticTest { + public static final String DOMAIN_DSN = "dc=zapodot,dc=org"; + + @RegisterExtension + public static EmbeddedLdapExtension embeddedLdapExtension = EmbeddedLdapExtensionBuilder + .newInstance() + .usingDomainDsn(DOMAIN_DSN) + .usingBindDSN("cn=Directory manager") + .usingBindCredentials("testPass") + .importingLdifs("example.ldif") + .build(); + + @Test + void testCheck() throws Exception { + final LDAPInterface ldapConnection = embeddedLdapExtension.ldapConnection(); + assertEquals(4, ldapConnection.search(DOMAIN_DSN, SearchScope.SUB, "(objectClass=*)").getEntryCount()); + + } +} diff --git a/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionTest.java b/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionTest.java new file mode 100644 index 0000000..d2e40f7 --- /dev/null +++ b/src/test/java/org/zapodot/junit/ldap/junit5/EmbeddedLdapExtensionTest.java @@ -0,0 +1,94 @@ +package org.zapodot.junit.ldap.junit5; + +import com.google.common.collect.Iterators; +import com.unboundid.ldap.sdk.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.*; + +class EmbeddedLdapExtensionTest { + + public static final String DOMAIN_DSN = "dc=zapodot,dc=org"; + + @RegisterExtension + public EmbeddedLdapExtension embeddedLdapExtension = EmbeddedLdapExtensionBuilder + .newInstance() + .usingDomainDsn(DOMAIN_DSN) + .importingLdifs("example.ldif") + .build(); + + @Test + void testLdapConnection() throws Exception { + final LDAPInterface ldapConnection = embeddedLdapExtension.ldapConnection(); + final SearchResult searchResult = ldapConnection.search(DOMAIN_DSN, SearchScope.SUB, "(objectClass=person)"); + assertEquals(1, searchResult.getEntryCount()); + } + + @Test + void testRawLdapConnection() throws Exception { + final String commonName = "Test person"; + final String dn = String.format( + "cn=%s,ou=people,dc=zapodot,dc=org", + commonName); + LDAPConnection ldapConnection = embeddedLdapExtension.unsharedLdapConnection(); + try { + ldapConnection.add(new AddRequest(dn, Arrays.asList( + new Attribute("objectclass", "top", "person", "organizationalPerson", "inetOrgPerson"), + new Attribute("cn", commonName), new Attribute("sn", "Person"), new Attribute("uid", "test")))); + } finally { + // Forces the LDAP connection to be closed. This is not necessary as the rule will usually close it for you. + ldapConnection.close(); + } + ldapConnection = embeddedLdapExtension.unsharedLdapConnection(); + final SearchResultEntry entry = ldapConnection.searchForEntry(new SearchRequest(dn, + SearchScope.BASE, + "(objectClass=person)")); + assertNotNull(entry); + } + + @Test + void testDirContext() throws Exception { + final DirContext dirContext = embeddedLdapExtension.dirContext(); + final SearchControls searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + final NamingEnumeration resultNamingEnumeration = + dirContext.search(DOMAIN_DSN, "(objectClass=person)", searchControls); + assertEquals(1, Iterators.size(Iterators.forEnumeration(resultNamingEnumeration))); + } + + @Test + void testContext() throws Exception { + final Context context = embeddedLdapExtension.context(); + final Object user = context.lookup("cn=Sondre Eikanger Kvalo,ou=people,dc=zapodot,dc=org"); + assertNotNull(user); + } + + @Test + void testContextClose() throws Exception { + final Context context = embeddedLdapExtension.context(); + context.close(); + assertNotNull(context.getNameInNamespace()); + + } + + @Test + void testEmbeddedServerPort() { + assertTrue(embeddedLdapExtension.embeddedServerPort() > 0); + + } + + @Test + void testNoPortAssignedYet() { + final EmbeddedLdapExtension embeddedLdapRule = new EmbeddedLdapExtensionBuilder().build(); + + assertThrows(IllegalStateException.class, embeddedLdapRule::embeddedServerPort); + + } +}