Skip to content

Instantly share code, notes, and snippets.

@fbiville
Created August 27, 2017 17:28
Show Gist options
  • Save fbiville/fcd09cf818e461ba9dadac443940ddc7 to your computer and use it in GitHub Desktop.
Save fbiville/fcd09cf818e461ba9dadac443940ddc7 to your computer and use it in GitHub Desktop.
Neo4J embedded database JUnit rule that works consistenly against all Neo4j 3.x stable versions
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.BiFunction;
public class EmbeddedGraphDatabaseRule implements TestRule {
private static final SemanticVersion NEO4J_300 = SemanticVersion.parse("3.0.0");
private static final SemanticVersion NEO4J_310 = SemanticVersion.parse("3.1.0");
private final Object embeddedDatabaseRule;
public EmbeddedGraphDatabaseRule(SemanticVersion neo4jVersion) {
if (neo4jVersion.compareTo(NEO4J_300) < 0) {
throw new IllegalArgumentException("Only Neo4j 3.x is supported for this rule");
}
embeddedDatabaseRule = instantiateRule(neo4jVersion);
}
@Override
public Statement apply(Statement base, Description description) {
try {
return ((Statement) applyMethod(embeddedDatabaseRule.getClass()).invoke(embeddedDatabaseRule, base, description));
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw propagate(e, e.getMessage());
}
}
public <T> T doInTransaction(BiFunction<Transaction, GraphDatabaseService, T> function) {
GraphDatabaseService graphDatabaseService = getGraphDatabaseService();
try (Transaction transaction = graphDatabaseService.beginTx()) {
return function.apply(transaction, graphDatabaseService);
}
}
public GraphDatabaseService getGraphDatabaseService() {
return (GraphDatabaseService) embeddedDatabaseRule;
}
private Object instantiateRule(SemanticVersion neo4jVersion) {
try {
return loadClass(neo4jVersion).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
throw propagate(e, e.getMessage());
}
}
private Class<?> loadClass(SemanticVersion neo4jVersion) throws ClassNotFoundException {
if (neo4jVersion.compareTo(NEO4J_310) >= 0) {
return Class.forName("org.neo4j.test.rule.EmbeddedDatabaseRule");
}
return Class.forName("org.neo4j.test.EmbeddedDatabaseRule");
}
private Method applyMethod(Class<?> type) throws NoSuchMethodException {
return type.getMethod("apply", Statement.class, Description.class);
}
private RuntimeException propagate(Exception e, String message) {
if (e instanceof RuntimeException) {
return (RuntimeException) e;
}
return new RuntimeException(message, e);
}
}
import org.junit.Rule;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static java.util.stream.Collectors.toList;
public class ExampleTest {
// [...] CURRENT_NEO4J_VERSION can be any stable Neo4j 3.x version
@Rule public final EmbeddedGraphDatabaseRule graphDatabaseRule = new EmbeddedGraphDatabaseRule(CURRENT_NEO4J_VERSION);
@Test
public void incredible_fact() {
graphDatabaseRule.doInTransaction((tx, db) -> {
List<Long> values = db.execute("RETURN 42 AS wow")
.columnAs("wow")
.map(Object::toString)
.map(Long::valueOf)
.stream().collect(toList());
assertThat(values).containsExactly(42L);
});
}
}
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Incomplete semantic version datastructure
* It only checks for major.minor.patch and discards additional information
* such as pre-release version and build metadata.
* <a href="http://semver.org/spec/v2.0.0.html">SemVer 2.0.0</a>
*/
public class SemanticVersion implements Comparable<SemanticVersion> {
private static final Pattern STABLE_VERSION = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)");
private final int major;
private final int minor;
private final int patch;
public static SemanticVersion parse(String version) {
Matcher matcher = STABLE_VERSION.matcher(version);
if (!matcher.matches()) {
throw new IllegalArgumentException(String.format(
"The version must be stable and follow the pattern x.y.z. Actual: %s",
version
));
}
return new SemanticVersion(
Integer.parseInt(matcher.group(1), 10),
Integer.parseInt(matcher.group(2), 10),
Integer.parseInt(matcher.group(3), 10)
);
}
public SemanticVersion(int major, int minor, int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
}
@Override
public int compareTo(SemanticVersion version) {
if (this.major < version.major) {
return -42;
}
if (this.major > version.major) {
return 42;
}
if (this.minor < version.minor) {
return -42;
}
if (this.minor > version.minor) {
return 42;
}
if (this.patch < version.patch) {
return -42;
}
if (this.patch > version.patch) {
return 42;
}
return 0;
}
@Override
public int hashCode() {
return Objects.hash(major, minor, patch);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final SemanticVersion other = (SemanticVersion) obj;
return other.compareTo(this) == 0;
}
@Override
public String toString() {
return String.format("Version %d.%d.%d", major, minor, patch);
}
}
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
public class SemanticVersionTest {
@Rule public ExpectedException thrown = ExpectedException.none();
@Test
public void compares_major_versions() {
assertThat(SemanticVersion.parse("1.0.0"))
.isEqualTo(SemanticVersion.parse("1.0.0"));
assertThat(SemanticVersion.parse("1.0.0"))
.isLessThan(SemanticVersion.parse("2.0.0"));
assertThat(SemanticVersion.parse("3.0.0"))
.isGreaterThan(SemanticVersion.parse("2.0.0"));
}
@Test
public void compares_minor_versions() {
assertThat(SemanticVersion.parse("0.1.0"))
.isEqualTo(SemanticVersion.parse("0.1.0"));
assertThat(SemanticVersion.parse("0.1.0"))
.isLessThan(SemanticVersion.parse("0.2.0"));
assertThat(SemanticVersion.parse("0.3.0"))
.isGreaterThan(SemanticVersion.parse("0.2.0"));
}
@Test
public void compares_patch_versions() {
assertThat(SemanticVersion.parse("0.0.1"))
.isEqualTo(SemanticVersion.parse("0.0.1"));
assertThat(SemanticVersion.parse("0.0.1"))
.isLessThan(SemanticVersion.parse("0.0.2"));
assertThat(SemanticVersion.parse("0.0.3"))
.isGreaterThan(SemanticVersion.parse("0.0.2"));
}
@Test
public void fails_to_parses_invalid_method() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("the version must be stable and follow the pattern x.y.z. Actual: 1.2.3-SNAPSHOT");
SemanticVersion.parse("1.2.3-SNAPSHOT");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment