{#- Java test file template for JUnit 5
Context variables:
- header: file header comment
- java_group_id: Java package ID (e.g., "com.example.e2e")
- test_class_name: test class name (e.g., "BasicTest")
- category: category name from fixture group
- imports: list of import strings (pre-formatted)
- needs_object_mapper: boolean
- fixtures_body: string containing all rendered test methods
- uses_harness: boolean, true if server-pattern harness is active
#}
{{ header }}
package {{ java_group_id }}.e2e;
{% if imports %}
{% for imp in imports %}
{{ imp }}
{% endfor %}
{% endif %}
{%- if uses_harness %}
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import java.io.IOException;
import java.net.Socket;
import java.net.URI;
import java.time.Instant;
{%- endif %}
/** E2e tests for category: {{ category }}. */
public class {{ test_class_name }} {
{%- if needs_object_mapper %}
private static final ObjectMapper MAPPER = new ObjectMapper().registerModule(new Jdk8Module()).setPropertyNamingStrategy(com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE);
{%- endif %}
{%- if uses_harness %}
private static Process harnessProcess;
private static String sutUrl;
@BeforeAll
static void startHarness() throws Exception {
String preset = System.getenv("SUT_URL");
if (preset != null && !preset.isEmpty()) {
sutUrl = preset;
return;
}
String javaHome = System.getProperty("java.home");
ProcessBuilder pb = new ProcessBuilder(
javaHome + "/bin/java",
"-cp", System.getProperty("java.class.path"),
"{{ java_group_id }}.e2e.HarnessMain"
);
// Inherit parent process environment so native library paths are available
pb.environment().putAll(System.getenv());
pb.redirectErrorStream(false);
harnessProcess = pb.start();
// Drain harness output in background (optional port discovery, but primarily
// to prevent pipe blocking). The harness may emit HARNESS_PORT but stdout buffering
// when piped often prevents timely delivery, so we don't rely on it — instead we
// use a default port and TCP polling below.
java.io.BufferedReader reader = new java.io.BufferedReader(
new java.io.InputStreamReader(harnessProcess.getInputStream(), "UTF-8")
);
java.io.BufferedReader errReader = new java.io.BufferedReader(
new java.io.InputStreamReader(harnessProcess.getErrorStream(), "UTF-8")
);
Thread stderrDrainer = new Thread(() -> {
try {
String errLine;
while ((errLine = errReader.readLine()) != null) {
System.err.println("[Harness stderr] " + errLine);
}
} catch (java.io.IOException ignored) {}
});
stderrDrainer.setDaemon(true);
stderrDrainer.start();
Thread stdoutDrainer = new Thread(() -> {
try {
String outLine;
while ((outLine = reader.readLine()) != null) {
// Optionally parse HARNESS_PORT if present, but don't rely on it
if (outLine.startsWith("HARNESS_PORT=")) {
System.out.println("[Harness port marker] " + outLine);
}
}
} catch (java.io.IOException ignored) {}
});
stdoutDrainer.setDaemon(true);
stdoutDrainer.start();
// Use the default harness port and TCP polling to verify reachability
int harnessPort = 8000;
String host = "127.0.0.1";
int port = harnessPort;
sutUrl = "http://" + host + ":" + port;
// TCP-readiness probe: poll until the harness accepts connections on the configured port
long deadline = System.currentTimeMillis() + 15_000;
boolean ready = false;
while (System.currentTimeMillis() < deadline) {
if (harnessProcess.isAlive() == false) {
break;
}
try (Socket socket = new Socket(host, port)) {
ready = true;
break;
} catch (IOException e) {
Thread.sleep(100);
}
}
if (!ready) {
if (harnessProcess != null) {
harnessProcess.destroyForcibly();
}
throw new RuntimeException("Harness did not become reachable at " + sutUrl + " within 15s");
}
System.setProperty("SUT_URL", sutUrl);
}
@AfterAll
static void stopHarness() throws Exception {
if (harnessProcess != null && harnessProcess.isAlive()) {
harnessProcess.destroy();
if (!harnessProcess.waitFor(5, java.util.concurrent.TimeUnit.SECONDS)) {
harnessProcess.destroyForcibly();
}
}
}
{%- endif %}
{{ fixtures_body }}
}