HTTP boundry
All checks were successful
CI / React UI Build (push) Successful in 12s
CI / Native Windows Build And Tests (push) Successful in 2m22s
CI / Windows Release Package (push) Has been skipped

This commit is contained in:
Aiden
2026-05-30 20:34:52 +10:00
parent aa33d72b6e
commit 04e0802ef2
13 changed files with 247 additions and 165 deletions

View File

@@ -86,6 +86,7 @@ add_video_shader_test(RenderCadenceCompositorRuntimeStateJsonTests
)
add_video_shader_test(RenderCadenceCompositorHttpControlServerTests
"${SRC_DIR}/app/RenderCadenceHttpRoutes.cpp"
"${SRC_DIR}/control/RuntimeControlCommand.cpp"
"${SRC_DIR}/control/http/HttpControlServer.cpp"
"${SRC_DIR}/control/http/HttpControlServerRoutes.cpp"

View File

@@ -1,4 +1,5 @@
#include "HttpControlServer.h"
#include "RenderCadenceHttpRoutes.h"
#include "RuntimeControlCommand.h"
#include <filesystem>
@@ -45,20 +46,41 @@ void TestParsesHttpRequest()
ExpectEquals(request.headers["host"], "127.0.0.1", "headers are lower-cased and trimmed");
}
void TestStateEndpointUsesCallback()
void TestServerDelegatesToRouteCallback()
{
using namespace RenderCadenceCompositor;
HttpControlServer server;
HttpControlServerCallbacks callbacks;
callbacks.getStateJson = []() { return std::string("{\"ok\":true}"); };
callbacks.routeRequest = [](const HttpRequest& request) {
ExpectEquals(request.path, "/custom", "generic route callback receives request path");
return HttpControlServer::JsonResponse("200 OK", "{\"custom\":true}");
};
server.SetCallbacksForTest(callbacks);
HttpControlServer::HttpRequest request;
request.method = "GET";
request.path = "/custom";
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
ExpectEquals(response.status, "200 OK", "server delegates HTTP request to route callback");
ExpectEquals(response.contentType, "application/json", "route callback controls content type");
ExpectEquals(response.body, "{\"custom\":true}", "route callback controls response body");
}
void TestStateEndpointUsesCallback()
{
using namespace RenderCadenceCompositor;
HttpControlServer server;
RenderCadenceHttpRouteCallbacks callbacks;
callbacks.getStateJson = []() { return std::string("{\"ok\":true}"); };
HttpControlServer::HttpRequest request;
request.method = "GET";
request.path = "/api/state";
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);
ExpectEquals(response.status, "200 OK", "state endpoint succeeds");
ExpectEquals(response.contentType, "application/json", "state endpoint is JSON");
ExpectEquals(response.body, "{\"ok\":true}", "state endpoint returns callback JSON");
@@ -69,15 +91,14 @@ void TestConfigEndpointUsesCallback()
using namespace RenderCadenceCompositor;
HttpControlServer server;
HttpControlServerCallbacks callbacks;
RenderCadenceHttpRouteCallbacks callbacks;
callbacks.getConfigJson = []() { return std::string("{\"diskLoaded\":true}"); };
server.SetCallbacksForTest(callbacks);
HttpControlServer::HttpRequest request;
request.method = "GET";
request.path = "/api/config";
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);
ExpectEquals(response.status, "200 OK", "config endpoint succeeds");
ExpectEquals(response.contentType, "application/json", "config endpoint is JSON");
ExpectEquals(response.body, "{\"diskLoaded\":true}", "config endpoint returns callback JSON");
@@ -88,15 +109,14 @@ void TestNdiSourcesEndpointUsesCallback()
using namespace RenderCadenceCompositor;
HttpControlServer server;
HttpControlServerCallbacks callbacks;
RenderCadenceHttpRouteCallbacks callbacks;
callbacks.getNdiSourcesJson = []() { return std::string("{\"ok\":true,\"sources\":[{\"name\":\"DESKTOP (Camera)\"}]}"); };
server.SetCallbacksForTest(callbacks);
HttpControlServer::HttpRequest request;
request.method = "GET";
request.path = "/api/ndi/sources";
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);
ExpectEquals(response.status, "200 OK", "NDI sources endpoint succeeds");
ExpectEquals(response.contentType, "application/json", "NDI sources endpoint is JSON");
Expect(response.body.find("DESKTOP (Camera)") != std::string::npos, "NDI sources endpoint returns callback JSON");
@@ -127,7 +147,8 @@ void TestRootServesUiIndex()
request.path = "/";
server.SetRootsForTest(root, std::filesystem::path());
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
RenderCadenceHttpRouteCallbacks callbacks;
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);
ExpectEquals(response.status, "200 OK", "root endpoint serves UI index");
ExpectEquals(response.contentType, "text/html", "UI index content type is html");
@@ -146,7 +167,8 @@ void TestKnownPostEndpointReturnsActionError()
request.path = "/api/layers/add";
request.body = "{\"shaderId\":\"happy-accident\"}";
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
RenderCadenceHttpRouteCallbacks callbacks;
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);
ExpectEquals(response.status, "400 Bad Request", "unimplemented post returns OpenAPI action error status");
ExpectEquals(response.contentType, "application/json", "unimplemented post returns JSON");
Expect(response.body.find("\"ok\":false") != std::string::npos, "unimplemented post reports ok false");
@@ -158,7 +180,7 @@ void TestLayerPostEndpointsUseCallbacks()
using namespace RenderCadenceCompositor;
HttpControlServer server;
HttpControlServerCallbacks callbacks;
RenderCadenceHttpRouteCallbacks callbacks;
callbacks.addLayer = [](const std::string& body) {
Expect(body.find("solid") != std::string::npos, "add callback receives request body");
return ControlActionResult{ true, std::string() };
@@ -167,13 +189,12 @@ void TestLayerPostEndpointsUseCallbacks()
Expect(body.find("runtime-layer-1") != std::string::npos, "remove callback receives request body");
return ControlActionResult{ false, "Unknown layer id." };
};
server.SetCallbacksForTest(callbacks);
HttpControlServer::HttpRequest addRequest;
addRequest.method = "POST";
addRequest.path = "/api/layers/add";
addRequest.body = "{\"shaderId\":\"solid\"}";
const HttpControlServer::HttpResponse addResponse = server.RouteRequestForTest(addRequest);
const HttpControlServer::HttpResponse addResponse = RouteRenderCadenceHttpRequest(addRequest, server, callbacks);
ExpectEquals(addResponse.status, "200 OK", "add layer callback success returns 200");
Expect(addResponse.body.find("\"ok\":true") != std::string::npos, "add layer callback returns action success");
@@ -181,7 +202,7 @@ void TestLayerPostEndpointsUseCallbacks()
removeRequest.method = "POST";
removeRequest.path = "/api/layers/remove";
removeRequest.body = "{\"layerId\":\"runtime-layer-1\"}";
const HttpControlServer::HttpResponse removeResponse = server.RouteRequestForTest(removeRequest);
const HttpControlServer::HttpResponse removeResponse = RouteRenderCadenceHttpRequest(removeRequest, server, callbacks);
ExpectEquals(removeResponse.status, "400 Bad Request", "remove layer callback failure returns 400");
Expect(removeResponse.body.find("Unknown layer id.") != std::string::npos, "remove layer callback returns diagnostic");
}
@@ -191,20 +212,19 @@ void TestGenericPostCallbackHandlesControlRoutes()
using namespace RenderCadenceCompositor;
HttpControlServer server;
HttpControlServerCallbacks callbacks;
RenderCadenceHttpRouteCallbacks callbacks;
callbacks.executePost = [](const std::string& path, const std::string& body) {
ExpectEquals(path, "/api/layers/set-bypass", "generic callback receives route path");
Expect(body.find("runtime-layer-1") != std::string::npos, "generic callback receives request body");
return ControlActionResult{ true, std::string() };
};
server.SetCallbacksForTest(callbacks);
HttpControlServer::HttpRequest request;
request.method = "POST";
request.path = "/api/layers/set-bypass";
request.body = "{\"layerId\":\"runtime-layer-1\",\"bypass\":true}";
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);
ExpectEquals(response.status, "200 OK", "generic control callback success returns 200");
Expect(response.body.find("\"ok\":true") != std::string::npos, "generic control callback returns action success");
}
@@ -214,20 +234,19 @@ void TestGenericPostCallbackHandlesConfigRoutes()
using namespace RenderCadenceCompositor;
HttpControlServer server;
HttpControlServerCallbacks callbacks;
RenderCadenceHttpRouteCallbacks callbacks;
callbacks.executePost = [](const std::string& path, const std::string& body) {
ExpectEquals(path, "/api/config/save", "generic callback receives config route path");
Expect(body.find("runtimeShaderId") != std::string::npos, "generic callback receives config request body");
return ControlActionResult{ true, std::string() };
};
server.SetCallbacksForTest(callbacks);
HttpControlServer::HttpRequest request;
request.method = "POST";
request.path = "/api/config/save";
request.body = "{\"runtimeShaderId\":\"solid-color\"}";
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);
ExpectEquals(response.status, "200 OK", "config save callback success returns 200");
Expect(response.body.find("\"ok\":true") != std::string::npos, "config save callback returns action success");
}
@@ -251,7 +270,8 @@ void TestUnknownEndpointReturns404()
request.method = "GET";
request.path = "/api/nope";
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
RenderCadenceHttpRouteCallbacks callbacks;
const HttpControlServer::HttpResponse response = RouteRenderCadenceHttpRequest(request, server, callbacks);
ExpectEquals(response.status, "404 Not Found", "unknown endpoint returns 404");
}
}
@@ -259,6 +279,7 @@ void TestUnknownEndpointReturns404()
int main()
{
TestParsesHttpRequest();
TestServerDelegatesToRouteCallback();
TestStateEndpointUsesCallback();
TestConfigEndpointUsesCallback();
TestNdiSourcesEndpointUsesCallback();