{#- C# app harness for server-pattern e2e tests
This harness program is spawned as a subprocess by TestSetup.cs and runs the
SUT app, registering handlers per fixture. It loads all fixtures, creates
handlers that return expected responses, and serves on a configured port.
Context variables (passed from C# codegen):
- imports: list of module names to import (e.g., ["MyPkg"])
- app_class: class name for SUT app (e.g., "App")
- register_method: method to register routes (e.g., "route")
- route_builder_class: class name for route builder (e.g., "RouteBuilder")
- method_enum_class: class name for method enum (e.g., "Method")
- run_method: serve entrypoint (e.g., "run")
- host: binding host (e.g., "127.0.0.1")
- port: binding port (e.g., 8000)
- fixtures_json: raw JSON string with all fixtures (auto-serialized)
#}
{{ header }}using System;
using System.Collections.Generic;
using System.Text.Json;
{% for import_name in imports %}
using {{ import_name }};
{% endfor %}
class Program
{
static void Main(string[] args)
{
// Load fixtures from JSON.
var fixturesJson = @"{{ fixtures_json }}";
var fixtures = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(fixturesJson)
?? new Dictionary<string, JsonElement>();
// Create and configure the app.
var app = new {{ app_class }}();
// Register a handler for each fixture.
foreach (var (fixtureId, fixture) in fixtures)
{
if (!fixture.TryGetProperty("http", out var httpElement))
continue;
var http = httpElement;
if (!http.TryGetProperty("handler", out var handler))
continue;
var route = handler.TryGetProperty("route", out var routeElem) ? routeElem.GetString() ?? "/" : "/";
var methodStr = handler.TryGetProperty("method", out var methodElem) ? methodElem.GetString() ?? "GET" : "GET";
var bodySchema = handler.TryGetProperty("body_schema", out var bodySchemaElem) && bodySchemaElem.ValueKind != JsonValueKind.Null
? bodySchemaElem.GetRawText()
: null;
var expectedResponse = http.TryGetProperty("expected_response", out var expectedElem) ? expectedElem : new JsonElement();
var expectedStatus = expectedResponse.TryGetProperty("status_code", out var statusElem) ? statusElem.GetInt32() : 200;
var expectedBody = expectedResponse.TryGetProperty("{{ response_body_field }}", out var bodyElem) && bodyElem.ValueKind != JsonValueKind.Null
? bodyElem
: JsonSerializer.SerializeToElement((object?)null);
var expectedHeaders = expectedResponse.TryGetProperty("headers", out var headersElem) && headersElem.ValueKind == JsonValueKind.Object
? headersElem
: new JsonElement();
// Build handler function that returns the expected response.
Func<string, Dictionary<string, IEnumerable<string>>> handlerFn = (_) =>
{
var responseHeaders = new Dictionary<string, IEnumerable<string>>();
if (expectedHeaders.ValueKind == JsonValueKind.Object)
{
foreach (var prop in expectedHeaders.EnumerateObject())
{
responseHeaders[prop.Name] = new[] { prop.Value.GetString() ?? string.Empty };
}
}
var statusStr = expectedStatus.ToString();
var contentStr = expectedBody.ValueKind != JsonValueKind.Null ? expectedBody.GetRawText() : null;
// Return a dict shaped like the SUT's Response for JSON serialization.
return new Dictionary<string, dynamic>
{
{ "status_code", expectedStatus },
{ "{{ response_body_field }}", contentStr },
{ "headers", responseHeaders }
} as dynamic as Dictionary<string, IEnumerable<string>>;
};
// Register the route with the handler.
var fullRoute = $"/fixtures/{fixtureId}{route}";
var methodEnum = Enum.Parse<{{ method_enum_class }}>(methodStr, ignoreCase: true);
var builder = new {{ route_builder_class }}(methodEnum, fullRoute);
if (bodySchema != null)
builder = builder.RequestSchemaJson(bodySchema);
app.{{ register_method }}(builder, handlerFn);
}
// Configure and start the server.
var config = new ServerConfig { Host = "{{ host }}", Port = {{ port }} };
app.config(config);
Console.WriteLine("Harness listening on {{ host }}:{{ port }}");
System.Console.Out.Flush();
app.{{ run_method }}();
}
}