Created
August 27, 2017 17:28
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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