Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[c11DvQNF] Investigate flaky test #556

Merged
merged 4 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions common/src/main/java/apoc/export/util/ExportConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,9 @@ private void validate() {
+ this.format + "]");
}
// CSV doesn't use optimization type
if (!OptimizationType.NONE.equals(this.optimizationType) && this.unwindBatchSize > this.batchSize
&& !ExportFormat.CSV.equals(this.format)) {
if (!OptimizationType.NONE.equals(this.optimizationType)
&& this.unwindBatchSize > this.batchSize
&& !ExportFormat.CSV.equals(this.format)) {
throw new RuntimeException("`unwindBatchSize` must be <= `batchSize`, but got [unwindBatchSize:"
+ unwindBatchSize + ", batchSize:" + batchSize + "]");
}
Expand Down
1 change: 1 addition & 0 deletions common/src/main/java/apoc/export/util/ExportFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public enum ExportFormat {
public String getFormat() {
return format;
}

public static final ExportFormat fromString(String format) {
if (format != null && !format.isEmpty()) {
for (ExportFormat exportFormat : ExportFormat.values()) {
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/apoc/cypher/CypherInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ public void available() {
userLog.error("error upon initialization, invalid query: " + query);
}
}
} catch (Exception e) {
userLog.error("error upon initialization", e);
} finally {
finished = true;
}
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/java/apoc/export/csv/ExportCSV.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ public Stream<ProgressInfo> graph(
Collection<Node> nodes = (Collection<Node>) graph.get("nodes");
Collection<Relationship> rels = (Collection<Relationship>) graph.get("relationships");
String source = String.format("graph: nodes(%d), rels(%d)", nodes.size(), rels.size());
return exportCsv(fileName, source, new NodesAndRelsSubGraph(tx, nodes, rels), new ExportConfig(config, ExportFormat.CSV));
return exportCsv(
fileName,
source,
new NodesAndRelsSubGraph(tx, nodes, rels),
new ExportConfig(config, ExportFormat.CSV));
}

@NotThreadSafe
Expand Down
7 changes: 3 additions & 4 deletions core/src/test/java/apoc/export/csv/ExportCsvTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -485,16 +485,15 @@ private void assertCsvCommon(String fileName, Map<String, Object> r) {

@Test
public void testExportAllCsvStreaming() {
String statement =
"CALL apoc.export.csv.all(null,{stream:true,batchSize:2})";
String statement = "CALL apoc.export.csv.all(null,{stream:true,batchSize:2})";
assertExportStreaming(statement, NONE);
}

@Test
public void testExportAllCsvStreamingCompressed() {
final CompressionAlgo algo = GZIP;
String statement = "CALL apoc.export.csv.all(null, {compression: '" + algo.name()
+ "',stream:true,batchSize:2})";
String statement =
"CALL apoc.export.csv.all(null, {compression: '" + algo.name() + "',stream:true,batchSize:2})";
assertExportStreaming(statement, algo);
}

Expand Down
46 changes: 25 additions & 21 deletions it/src/test/java/apoc/it/core/StartupTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@

import static apoc.util.TestContainerUtil.createDB;
import static apoc.util.TestContainerUtil.dockerImageForNeo4j;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.neo4j.test.assertion.Assert.assertEventually;

import apoc.ApocSignatures;
import apoc.util.Neo4jContainerExtension;
Expand Down Expand Up @@ -100,23 +101,26 @@ public void check_basic_deployment() {
public void check_cypherInitializer_waits_for_systemDb_to_be_available() {
// we check that with apoc-core jar and all extra-dependencies jars every procedure/function is detected
var version = Neo4jVersion.ENTERPRISE;
Neo4jContainerExtension neo4jContainer = createDB(version, List.of(ApocPackage.CORE), !TestUtil.isRunningInCI())
.withEnv(
"apoc.initializer.system.0",
"CREATE USER dummy IF NOT EXISTS SET PASSWORD \"pass12345\" CHANGE NOT REQUIRED")
.withEnv("apoc.initializer.system.1", "GRANT ROLE reader TO dummy");
try {
Neo4jContainerExtension neo4jContainer = createDB(
version, List.of(ApocPackage.CORE), !TestUtil.isRunningInCI())
.withEnv(
"apoc.initializer.system.0",
"CREATE USER dummy IF NOT EXISTS SET PASSWORD \"pass12345\" CHANGE NOT REQUIRED")
.withEnv("apoc.initializer.system.1", "GRANT ROLE reader TO dummy");
neo4jContainer.start();

try (Session session = neo4jContainer.getSession()) {
assertEventually(
() -> session.run("SHOW USERS YIELD roles, user WHERE user = 'dummy' RETURN roles").stream()
.collect(Collectors.toList()),
(result) -> result.size() > 0
&& result.get(0).get("roles").asList().contains("reader"),
30,
TimeUnit.SECONDS);
await("user has correct roles")
.atMost(30, TimeUnit.SECONDS)
.pollDelay(200, TimeUnit.MILLISECONDS)
.pollInSameThread()
.untilAsserted(() -> {
var result = session.run("SHOW USERS YIELD roles, user WHERE user = 'dummy' RETURN roles")
.list();
assertThat(result).anySatisfy(row -> {
assertThat(row.get("roles").asList()).contains("reader");
});
});
}

String logs = neo4jContainer.getLogs();
Expand All @@ -126,15 +130,15 @@ public void check_cypherInitializer_waits_for_systemDb_to_be_available() {
assertTrue(logs.contains("successfully initialized: GRANT ROLE reader TO dummy"));
// The password should have been redacted
assertFalse(logs.contains("pass12345"));
neo4jContainer.close();
} catch (Exception ex) {
if (TestContainerUtil.isDockerImageAvailable(ex)) {
ex.printStackTrace();
fail("Should not have thrown exception when trying to start Neo4j: " + ex);
} else {
fail("The docker image " + dockerImageForNeo4j(version)
+ " could not be loaded. Check whether it's available locally / in the CI. Exception:" + ex);
try {
neo4jContainer.dumpLogs();
} catch (Exception e) {
// Ignore
}
throw ex;
} finally {
neo4jContainer.close();
}
}

Expand Down
42 changes: 31 additions & 11 deletions test-utils/src/main/java/apoc/util/Neo4jContainerExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import static apoc.util.TestContainerUtil.Neo4jVersion.ENTERPRISE;

import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Scanner;
import org.neo4j.driver.AuthToken;
Expand Down Expand Up @@ -51,13 +53,16 @@ public class Neo4jContainerExtension extends Neo4jContainer<Neo4jContainerExtens
private boolean withDriver = true;

private boolean isRunning = false;
private final Path logsDir;

public Neo4jContainerExtension() {
super();
this.logsDir = null;
}

public Neo4jContainerExtension(String dockerImage) {
public Neo4jContainerExtension(String dockerImage, Path logsDir) {
setDockerImageName(dockerImage);
this.logsDir = logsDir;
}

public Neo4jContainerExtension withInitScript(String filePath) {
Expand All @@ -83,20 +88,35 @@ public void start() {
}
isRunning = true;
} catch (Exception startException) {
try {
System.out.println(this.execInContainer("cat", "logs/debug.log").toString());
System.out.println(this.execInContainer("cat", "logs/http.log").toString());
System.out.println(
this.execInContainer("cat", "logs/security.log").toString());
} catch (Exception ex) {
// we addSuppressed the exception produced by execInContainer, but we finally throw the original
// `startException`
startException.addSuppressed(new RuntimeException("Exception during fallback execInContainer", ex));
}
dumpLogs();
throw startException;
}
}

public void dumpLogs() {
try {
if (logsDir != null && Files.exists(logsDir)) {
System.err.println("--- Dumping logs ---");
System.err.println();
for (final var logFile : Files.list(logsDir).toList()) {
System.err.println(logFile.toString() + ":");
System.err.println(Files.readString(logFile));
System.err.println();
System.err.println();
}
System.err.println("--- No more logs ---");
} else if (isRunning) {
System.err.println(execInContainer("cat", "logs/debug.log").toString());
System.err.println(execInContainer("cat", "logs/http.log").toString());
System.err.println(execInContainer("cat", "logs/security.log").toString());
} else {
System.err.println("Failed to find logs");
}
} catch (Exception e) {
System.err.println("Failed to dump logs: " + e.getMessage());
}
}

private void executeScript(String filePath) {
InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
if (resource == null) {
Expand Down
12 changes: 11 additions & 1 deletion test-utils/src/main/java/apoc/util/TestContainerUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.*;
Expand Down Expand Up @@ -125,6 +127,13 @@ private static Neo4jContainerExtension createNeo4jContainer(
pluginsFolder.mkdirs();
String canonicalPath = null;

final Path logsDir;
try {
logsDir = Files.createTempDirectory("neo4j-logs");
} catch (IOException e) {
throw new RuntimeException(e);
}

try {
canonicalPath = importFolder.getCanonicalPath();
} catch (IOException e) {
Expand Down Expand Up @@ -156,7 +165,7 @@ private static Neo4jContainerExtension createNeo4jContainer(
}

System.out.println("neo4jDockerImageVersion = " + dockerImage);
Neo4jContainerExtension neo4jContainer = new Neo4jContainerExtension(dockerImage)
Neo4jContainerExtension neo4jContainer = new Neo4jContainerExtension(dockerImage, logsDir)
.withAdminPassword(password)
.withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes")
.withEnv("apoc.export.file.enabled", "true")
Expand All @@ -168,6 +177,7 @@ private static Neo4jContainerExtension createNeo4jContainer(
.withNeo4jConfig("dbms.logs.debug.level", "DEBUG")
.withNeo4jConfig("dbms.routing.driver.logging.level", "DEBUG")
.withNeo4jConfig("internal.dbms.type_constraints", "true")
.withFileSystemBind(logsDir.toString(), "/logs")
.withFileSystemBind(
canonicalPath, "/var/lib/neo4j/import") // map the "target/import" dir as the Neo4j's import dir
.withCreateContainerCmdModifier(cmd -> cmd.withMemory(2024 * 1024 * 1024L)) // 2gb
Expand Down
Loading