Skip to content

Commit 5ad8fa1

Browse files
committed
Add support for JUnit 5 extensions (#57)
1 parent d43da04 commit 5ad8fa1

14 files changed

+792
-426
lines changed

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# embedded-ldap-junit
22
[![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)
33

4-
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).
4+
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).
55

66
## Why?
77
* 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
4646
libraryDependencies += "org.zapodot" % "embedded-ldap-junit" % "0.8.1"
4747
```
4848

49-
### Add to Junit test
49+
### Add to JUnit test
50+
51+
#### JUnit 4
5052
```java
5153
import com.unboundid.ldap.sdk.LDAPInterface;
5254
import javax.naming.Context;
@@ -96,3 +98,19 @@ public void testContext() throws Exception {
9698
assertNotNull(user);
9799
}
98100
```
101+
102+
#### JUnit 5
103+
For JUnit 5 tests, simply use `EmbeddedLdapExtensionBuilder` instead of `EmbeddedLdapRuleBuilder`:
104+
105+
```java
106+
@RegisterExtension
107+
public EmbeddedLdapExtension embeddedLdapRule = EmbeddedLdapExtensionBuilder
108+
.newInstance()
109+
.usingDomainDsn("dc=example,dc=com")
110+
.importingLdifs("example.ldif")
111+
.build();
112+
113+
...
114+
```
115+
116+
Both JUnit 4 and JUnit 5 builders share the same API, as do the rule and the extension.

pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
</developers>
2525
<properties>
2626
<junit.version>4.13</junit.version>
27+
<junit5.version>5.7.0</junit5.version>
2728
<unboundid-ldapsdk.version>5.0.1</unboundid-ldapsdk.version>
2829
<slf4j.version>1.7.30</slf4j.version>
2930
<guava.version>29.0-jre</guava.version>
@@ -52,6 +53,12 @@
5253
<artifactId>junit</artifactId>
5354
<version>${junit.version}</version>
5455
</dependency>
56+
<dependency>
57+
<groupId>org.junit.jupiter</groupId>
58+
<artifactId>junit-jupiter-api</artifactId>
59+
<version>${junit5.version}</version>
60+
<optional>true</optional>
61+
</dependency>
5562
<dependency>
5663
<groupId>com.google.guava</groupId>
5764
<artifactId>guava</artifactId>
@@ -264,4 +271,4 @@
264271
</properties>
265272
</profile>
266273
</profiles>
267-
</project>
274+
</project>
Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,10 @@
11
package org.zapodot.junit.ldap;
22

3-
import com.unboundid.ldap.sdk.LDAPConnection;
4-
import com.unboundid.ldap.sdk.LDAPException;
5-
import com.unboundid.ldap.sdk.LDAPInterface;
63
import org.junit.rules.TestRule;
74

8-
import javax.naming.Context;
9-
import javax.naming.NamingException;
10-
import javax.naming.directory.DirContext;
11-
125
/**
136
* A JUnit rule that may be used as either a @Rule or a @ClassRule
147
*/
15-
public interface EmbeddedLdapRule extends TestRule {
16-
17-
/**
18-
* For tests depending on the UnboundID LDAP SDK. Returns a proxied version of an Unboundid interface that will be
19-
* closed when the test(s) have been invoked
20-
*
21-
* @return a shared LDAPConnection
22-
* @throws LDAPException if a connection can not be opened
23-
*/
24-
LDAPInterface ldapConnection() throws LDAPException;
25-
26-
/**
27-
* For tests depending on the UnboundID LDAP SDK that needs access to an ${link LDAPConnection} object
28-
* rather than the interface. If your code does not close the connection for you it will be closed on teardown
29-
*
30-
* @return a LDAPConnection connected to the embedded LDAP server
31-
* @throws LDAPException if an exception occurred while establishing the connection
32-
*/
33-
LDAPConnection unsharedLdapConnection() throws LDAPException;
34-
35-
/**
36-
* For tests depending on the standard Java JNDI API
37-
*
38-
* @return a shared Context connected to the in-memory LDAP server
39-
* @throws NamingException if context can not be created
40-
*/
41-
Context context() throws NamingException;
42-
43-
/**
44-
* Like {@link #context()}, but returns a DirContext
45-
*
46-
* @return a DirContext connected to the in-memory LDAP server
47-
* @throws NamingException if a LDAP failure happens during DirContext creation
48-
*/
49-
DirContext dirContext() throws NamingException;
50-
51-
/**
52-
* Gives access to the listening port for the currently running embedded LDAP server.
53-
* This will make it easier to use other integration mechanisms
54-
* <p>
55-
* Note: the embedded LDAP server is by default configured to listen only on the loopback address
56-
* (i.e <em>localhost/127.0.0.1</em>) unless another address has been provided to the builder when the rule was built
57-
* </p>
58-
*
59-
* @return the port number that the embedded server is listening to
60-
* @see org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder#bindingToAddress(String)
61-
*/
62-
int embeddedServerPort();
63-
8+
public interface EmbeddedLdapRule extends EmbeddedLdapServer, TestRule {
649

6510
}
Lines changed: 4 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,14 @@
11
package org.zapodot.junit.ldap;
22

3-
import com.google.common.base.Function;
4-
import com.google.common.base.Optional;
5-
import com.google.common.collect.Lists;
6-
import com.google.common.io.Resources;
7-
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
8-
import com.unboundid.ldap.listener.InMemoryListenerConfig;
9-
import com.unboundid.ldap.sdk.LDAPException;
10-
import com.unboundid.ldap.sdk.schema.Schema;
11-
import com.unboundid.ldif.LDIFException;
12-
import org.zapodot.junit.ldap.internal.AuthenticationConfiguration;
3+
import org.zapodot.junit.ldap.internal.AbstractEmbeddedLdapBuilder;
134
import org.zapodot.junit.ldap.internal.EmbeddedLdapRuleImpl;
145

15-
import java.io.File;
16-
import java.io.IOException;
17-
import java.net.InetAddress;
18-
import java.net.URISyntaxException;
19-
import java.net.UnknownHostException;
20-
import java.util.Arrays;
21-
import java.util.LinkedList;
22-
import java.util.List;
236
import java.util.Objects;
247

258
/**
269
* A builder providing a fluent way of defining EmbeddedLdapRule instances
2710
*/
28-
public class EmbeddedLdapRuleBuilder {
29-
30-
public static final String DEFAULT_DOMAIN = "dc=example,dc=com";
31-
public static final String DEFAULT_BIND_DSN = "cn=Directory manager";
32-
public static final String DEFAULT_BIND_CREDENTIALS = "password";
33-
public static final String LDAP_SERVER_LISTENER_NAME = "test-listener";
34-
public static final int MIN_PORT_EXCLUSIVE = 0;
35-
public static final int MAX_PORT_EXCLUSIVE = 65535;
36-
private List<String> domainDsn = new LinkedList<>();
37-
38-
private String bindDSN = DEFAULT_BIND_DSN;
39-
40-
private String bindCredentials = DEFAULT_BIND_CREDENTIALS;
41-
42-
private List<String> ldifsToImport = new LinkedList<>();
43-
44-
private List<String> schemaLdifs = new LinkedList<>();
45-
46-
private boolean addDefaultSchema = true;
47-
48-
private Integer bindPort = 0;
49-
50-
private InetAddress bindAddress = InetAddress.getLoopbackAddress();
51-
52-
private AuthenticationConfiguration authenticationConfiguration;
53-
54-
private InMemoryListenerConfig listenerConfig = null;
55-
56-
public EmbeddedLdapRuleBuilder() {
57-
}
11+
public class EmbeddedLdapRuleBuilder extends AbstractEmbeddedLdapBuilder<EmbeddedLdapRuleBuilder> {
5812

5913
/**
6014
* Creates a new builder
@@ -65,109 +19,8 @@ public static EmbeddedLdapRuleBuilder newInstance() {
6519
return new EmbeddedLdapRuleBuilder();
6620
}
6721

68-
/**
69-
* 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
70-
*
71-
* @param domainDsn a valid DSN string
72-
* @return same EmbeddedLdapRuleBuilder instance with the domainDsn field set
73-
*/
74-
public EmbeddedLdapRuleBuilder usingDomainDsn(final String domainDsn) {
75-
this.domainDsn.add(domainDsn);
76-
return this;
77-
}
78-
79-
/**
80-
* 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
81-
*
82-
* @param bindDSN a valid DSN string
83-
* @return same EmbeddedLdapRuleBuilder instance with the bindDSN field set
84-
*/
85-
public EmbeddedLdapRuleBuilder usingBindDSN(final String bindDSN) {
86-
this.bindDSN = bindDSN;
87-
return this;
88-
}
89-
90-
/**
91-
* 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
92-
*
93-
* @param bindCredentials a password string
94-
* @return same EmbeddedLdapRuleBuilder instance with the bindCredentials field set
95-
*/
96-
public EmbeddedLdapRuleBuilder usingBindCredentials(final String bindCredentials) {
97-
this.bindCredentials = bindCredentials;
98-
return this;
99-
}
100-
101-
/**
102-
* Sets the port that the in-memory LDAP server will bind to. If not set, an available port will be picked automatically
103-
*
104-
* @param port a port number
105-
* @return same EmbeddedLdapRuleBuilder instance with the port field set
106-
* @throws IllegalArgumentException if the provided value for port is not between @{link MIN_PORT_EXCLUSIVE}
107-
* and @{MAX_PORT_EXCLUSIVE} (exclusive)
108-
*/
109-
public EmbeddedLdapRuleBuilder bindingToPort(final int port) {
110-
if ((port < MIN_PORT_EXCLUSIVE) || (port > MAX_PORT_EXCLUSIVE)) {
111-
throw new IllegalArgumentException(String.format("Value \"%s\" is not a valid port number", port));
112-
}
113-
this.bindPort = Integer.valueOf(port);
114-
return this;
115-
}
116-
117-
/**
118-
* Allows the listening address for the embedded LDAP server to be set. If not set it will bind to <em>localhost/127.0.0.1</em>.
119-
*
120-
* @param address a valid hostname or textual representation of an IP address
121-
* @return same EmbeddedLdapRuleBuilder instance with the bindAddress field set
122-
* @throws IllegalArgumentException if the value provided for \"address\" is invalid
123-
*/
124-
public EmbeddedLdapRuleBuilder bindingToAddress(final String address) {
125-
Objects.requireNonNull(address);
126-
try {
127-
final InetAddress addressByName = InetAddress.getByName(address);
128-
this.bindAddress = addressByName;
129-
} catch (UnknownHostException e) {
130-
throw new IllegalArgumentException(String.format("Unknown host address \"%s\"", address), e);
131-
}
132-
return this;
133-
}
134-
135-
/**
136-
* Avoid adding UnboundID's default schema that contains the most common LDAP elements defined through various RFC's.
137-
*
138-
* @return same EmbeddedLdapRuleBuilder instance with the withoutDefaultSchema field set to FALSE
139-
*/
140-
public EmbeddedLdapRuleBuilder withoutDefaultSchema() {
141-
this.addDefaultSchema = false;
142-
return this;
143-
}
144-
145-
/**
146-
* Define schemas to be used for the server. If not defined, UnboundID will set up a default schema.
147-
*
148-
* @param ldifSchemaFiles LDIF-files containing schema element definitions
149-
* @return same EmbeddedLdapRuleBuilder with the given LDIF-files added to the internal schema file collection.
150-
*/
151-
public EmbeddedLdapRuleBuilder withSchema(final String... ldifSchemaFiles) {
152-
this.schemaLdifs.addAll(Arrays.asList(ldifSchemaFiles));
153-
return this;
154-
}
155-
156-
/**
157-
* Specify one or more LDIF resources to be imported on startup.
158-
*
159-
* @param ldifFiles LDIF-files to import
160-
* @return same EmbeddedLdapRuleBuilder instance with the provided ldifFiles added to the list of LDIF files to import
161-
*/
162-
public EmbeddedLdapRuleBuilder importingLdifs(final String... ldifFiles) {
163-
if (ldifFiles != null) {
164-
ldifsToImport.addAll(Arrays.asList(ldifFiles));
165-
}
166-
return this;
167-
}
168-
169-
public EmbeddedLdapRuleBuilder withListener(InMemoryListenerConfig listenerConfig) {
170-
this.listenerConfig = listenerConfig;
22+
@Override
23+
protected EmbeddedLdapRuleBuilder getThis() {
17124
return this;
17225
}
17326

@@ -183,82 +36,4 @@ public EmbeddedLdapRule build() {
18336
ldifsToImport);
18437
}
18538

186-
private InMemoryDirectoryServerConfig createInMemoryServerConfiguration() {
187-
try {
188-
final InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig =
189-
new InMemoryDirectoryServerConfig(domainDsnArray());
190-
191-
if (bindCredentials != null) {
192-
this.authenticationConfiguration = new AuthenticationConfiguration(bindDSN, bindCredentials);
193-
inMemoryDirectoryServerConfig.addAdditionalBindCredentials(bindDSN, bindCredentials);
194-
}
195-
196-
if (listenerConfig == null) {
197-
listenerConfig = InMemoryListenerConfig.createLDAPConfig(
198-
LDAP_SERVER_LISTENER_NAME,
199-
bindAddress,
200-
bindPort,
201-
null);
202-
}
203-
inMemoryDirectoryServerConfig.setListenerConfigs(listenerConfig);
204-
inMemoryDirectoryServerConfig.setSchema(customSchema());
205-
return inMemoryDirectoryServerConfig;
206-
} catch (LDAPException e) {
207-
throw new IllegalStateException(
208-
"Could not create configuration for the in-memory LDAP instance due to an exception",
209-
e);
210-
}
211-
}
212-
213-
private String[] domainDsnArray() {
214-
if (domainDsn.size() == 0) {
215-
return new String[]{DEFAULT_DOMAIN};
216-
} else {
217-
return domainDsn.toArray(new String[]{});
218-
}
219-
}
220-
221-
private Schema customSchema() {
222-
final List<File> schemaFiles = schemaFiles();
223-
224-
try {
225-
final Schema initialSchema = (addDefaultSchema ? Schema.getDefaultStandardSchema() : null);
226-
if (!schemaFiles.isEmpty()) {
227-
final Schema customSchema = initialSchema == null
228-
? Schema.getSchema(schemaFiles)
229-
: Schema.mergeSchemas(initialSchema, Schema.getSchema(schemaFiles));
230-
return customSchema;
231-
} else {
232-
return null;
233-
}
234-
235-
} catch (IOException | LDIFException | LDAPException e) {
236-
throw new IllegalArgumentException(
237-
"Could not create custom LDAP schema due, probably caused by an incorrectly formatted schema",
238-
e);
239-
}
240-
}
241-
242-
private List<File> schemaFiles() {
243-
return Lists.newArrayList(Lists.transform(this.schemaLdifs, new Function<String, File>() {
244-
@Override
245-
public File apply(final String input) {
246-
try {
247-
final File file = new File(Resources.getResource(input).toURI());
248-
if (!file.isFile()) {
249-
throw new IllegalArgumentException(String.format(
250-
"The resource named \"%s\" can not be found or is not a file",
251-
input));
252-
}
253-
return file;
254-
} catch (URISyntaxException e) {
255-
throw new IllegalArgumentException(String.format(
256-
"The resource named \"%s\" is not a valid file reference",
257-
input), e);
258-
}
259-
}
260-
}));
261-
}
262-
263-
26439
}

0 commit comments

Comments
 (0)