alef 0.22.8

Opinionated polyglot binding generator for Rust libraries
Documentation
{#- Java HarnessMain for server-pattern e2e tests

   This harness loads fixtures from classpath resources, registers HTTP handlers
   that return expected responses, and serves on a port from SUT_URL env var
   (or a configured default). It mirrors the Python app_harness.py pattern.

   Context variables:
   - java_group_id: Java package (e.g., "dev.my_pkg")
   - binding_pkg: binding package (e.g., "dev.my_pkg")
   - app_class: SUT app class name (e.g., "App")
   - run_method: serve entrypoint method name (e.g., "run")
   - register_method: app method to register routes (e.g., "registerAppRoute")
   - response_body_field: field name in Response for body (e.g., "body")
   - host: binding host (e.g., "127.0.0.1")
   - port: default binding port (e.g., 8000) — overridden by SUT_URL env var
   - fixture_ids: list of fixture IDs to register handlers for
#}
package {{ java_group_id }}.e2e;

import {{ binding_pkg }}.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.util.*;

public class HarnessMain {
    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static void main(String[] args) {
        try {
            // Resolve the port: read SUT_URL env var (e.g., http://127.0.0.1:8000)
            // or fall back to the configured default port
            String sutUrl = System.getenv("SUT_URL");
            int effectivePort = {{ port }};
            String effectiveHost = "{{ host }}";

            if (sutUrl != null && !sutUrl.isEmpty()) {
                try {
                    URI uri = new URI(sutUrl);
                    effectiveHost = uri.getHost() != null ? uri.getHost() : effectiveHost;
                    effectivePort = uri.getPort() > 0 ? uri.getPort() : effectivePort;
                } catch (Exception e) {
                    System.err.println("Warning: failed to parse SUT_URL: " + e.getMessage());
                }
            }

            // Create and configure the app
            {{ app_class }} app = new {{ app_class }}();

            // Register handlers for each fixture, loading from classpath resources.
            // Fixtures are stored as individual JSON files in src/test/resources/fixtures/
            // to avoid exceeding Java's 65KB string literal limit.
            List<String> fixtureIds = Arrays.asList(
{%- for fixture_id in fixture_ids %}
                "{{ fixture_id }}"{{ "," if not loop.last else "" }}
{%- endfor %}
            );

            for (String fixtureId : fixtureIds) {
                // Load fixture from classpath
                JsonNode fixtureData = FixtureLoader.loadFixture(fixtureId);
                if (fixtureData == null) {
                    System.err.println("Warning: could not load fixture " + fixtureId);
                    continue;
                }

                // Extract HTTP fixture data
                JsonNode httpNode = fixtureData.get("http");
                if (httpNode == null) {
                    continue;
                }

                // Extract handler and expected response details
                JsonNode handlerNode = httpNode.get("handler");
                JsonNode responseNode = httpNode.get("expected_response");

                if (handlerNode == null || responseNode == null) {
                    continue;
                }

                String route = handlerNode.get("route").asText();
                String method = handlerNode.get("method").asText();
                int statusCode = responseNode.get("status_code").asInt();
                JsonNode responseBody = responseNode.get("{{ response_body_field }}");
                JsonNode responseHeaders = responseNode.get("headers");

                // Build the full fixture-namespaced route: /fixtures/<fixture_id>{route}
                String fullRoute = "/fixtures/" + fixtureId + route;

                // Create a handler closure that captures the expected response for this fixture
                final int finalStatusCode = statusCode;
                final JsonNode finalResponseBody = responseBody;
                final JsonNode finalResponseHeaders = responseHeaders;
                Callable handler = request -> {
                    // Handler ignores request and returns the recorded expected response
                    Map<String, Object> resp = new LinkedHashMap<>();
                    resp.put("status_code", finalStatusCode);
                    resp.put("{{ response_body_field }}", finalResponseBody);
                    if (finalResponseHeaders != null && finalResponseHeaders.isObject()) {
                        Map<String, String> headers = new LinkedHashMap<>();
                        finalResponseHeaders.fieldNames().forEachRemaining(name ->
                            headers.put(name, finalResponseHeaders.get(name).asText())
                        );
                        resp.put("headers", headers);
                    }
                    return MAPPER.writeValueAsString(resp);
                };

                // Create RouteBuilder with the method enum and full route
                RouteBuilder builder = RouteBuilder.create(Method.valueOf(method), fullRoute);

                // Register the handler with the app
                int regResult = app.registerAppRoute(handler, builder);

                if (regResult != 0) {
                    System.err.println("Warning: failed to register fixture handler for " + fixtureId);
                }
            }

            // Signal that harness is ready and listening
            String harnessUrl = "http://" + effectiveHost + ":" + effectivePort;
            System.out.println("SUT_URL=" + harnessUrl);
            System.out.flush();

            // Run the app (blocks indefinitely)
            // The app is expected to listen on the configured port.
            app.{{ run_method }}();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}