183 lines
6.0 KiB
C++
183 lines
6.0 KiB
C++
#include "HttpControlServer.h"
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
namespace
|
|
{
|
|
int gFailures = 0;
|
|
|
|
void Expect(bool condition, const std::string& message)
|
|
{
|
|
if (condition)
|
|
return;
|
|
|
|
++gFailures;
|
|
std::cerr << "FAILED: " << message << "\n";
|
|
}
|
|
|
|
void ExpectEquals(const std::string& actual, const std::string& expected, const std::string& message)
|
|
{
|
|
if (actual == expected)
|
|
return;
|
|
|
|
++gFailures;
|
|
std::cerr << "FAILED: " << message << "\n"
|
|
<< "expected: " << expected << "\n"
|
|
<< "actual: " << actual << "\n";
|
|
}
|
|
|
|
void TestParsesHttpRequest()
|
|
{
|
|
using namespace RenderCadenceCompositor;
|
|
|
|
HttpControlServer::HttpRequest request;
|
|
const bool parsed = HttpControlServer::ParseHttpRequest(
|
|
"GET /api/state?cacheBust=1 HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n",
|
|
request);
|
|
|
|
Expect(parsed, "request parses");
|
|
ExpectEquals(request.method, "GET", "method is parsed");
|
|
ExpectEquals(request.path, "/api/state", "query string is stripped from path");
|
|
ExpectEquals(request.headers["host"], "127.0.0.1", "headers are lower-cased and trimmed");
|
|
}
|
|
|
|
void TestStateEndpointUsesCallback()
|
|
{
|
|
using namespace RenderCadenceCompositor;
|
|
|
|
HttpControlServer server;
|
|
HttpControlServerCallbacks callbacks;
|
|
callbacks.getStateJson = []() { return std::string("{\"ok\":true}"); };
|
|
server.SetCallbacksForTest(callbacks);
|
|
|
|
HttpControlServer::HttpRequest request;
|
|
request.method = "GET";
|
|
request.path = "/api/state";
|
|
|
|
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
|
|
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");
|
|
}
|
|
|
|
void TestWebSocketAcceptKey()
|
|
{
|
|
using namespace RenderCadenceCompositor;
|
|
|
|
const std::string acceptKey = HttpControlServer::WebSocketAcceptKey("dGhlIHNhbXBsZSBub25jZQ==");
|
|
ExpectEquals(acceptKey, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", "WebSocket accept key matches RFC example");
|
|
}
|
|
|
|
void TestRootServesUiIndex()
|
|
{
|
|
using namespace RenderCadenceCompositor;
|
|
|
|
const std::filesystem::path root = std::filesystem::temp_directory_path() / "render-cadence-compositor-ui-test";
|
|
std::filesystem::create_directories(root);
|
|
{
|
|
std::ofstream output(root / "index.html", std::ios::binary);
|
|
output << "<!doctype html><div id=\"root\"></div>";
|
|
}
|
|
|
|
HttpControlServer server;
|
|
HttpControlServer::HttpRequest request;
|
|
request.method = "GET";
|
|
request.path = "/";
|
|
|
|
server.SetRootsForTest(root, std::filesystem::path());
|
|
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
|
|
|
|
ExpectEquals(response.status, "200 OK", "root endpoint serves UI index");
|
|
ExpectEquals(response.contentType, "text/html", "UI index content type is html");
|
|
Expect(response.body.find("root") != std::string::npos, "UI index body is returned");
|
|
|
|
std::filesystem::remove_all(root);
|
|
}
|
|
|
|
void TestKnownPostEndpointReturnsActionError()
|
|
{
|
|
using namespace RenderCadenceCompositor;
|
|
|
|
HttpControlServer server;
|
|
HttpControlServer::HttpRequest request;
|
|
request.method = "POST";
|
|
request.path = "/api/layers/add";
|
|
request.body = "{\"shaderId\":\"happy-accident\"}";
|
|
|
|
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
|
|
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");
|
|
Expect(response.body.find("not implemented") != std::string::npos, "unimplemented post reports diagnostic");
|
|
}
|
|
|
|
void TestLayerPostEndpointsUseCallbacks()
|
|
{
|
|
using namespace RenderCadenceCompositor;
|
|
|
|
HttpControlServer server;
|
|
HttpControlServerCallbacks callbacks;
|
|
callbacks.addLayer = [](const std::string& body) {
|
|
Expect(body.find("solid") != std::string::npos, "add callback receives request body");
|
|
return ControlActionResult{ true, std::string() };
|
|
};
|
|
callbacks.removeLayer = [](const std::string& body) {
|
|
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);
|
|
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");
|
|
|
|
HttpControlServer::HttpRequest removeRequest;
|
|
removeRequest.method = "POST";
|
|
removeRequest.path = "/api/layers/remove";
|
|
removeRequest.body = "{\"layerId\":\"runtime-layer-1\"}";
|
|
const HttpControlServer::HttpResponse removeResponse = server.RouteRequestForTest(removeRequest);
|
|
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");
|
|
}
|
|
|
|
void TestUnknownEndpointReturns404()
|
|
{
|
|
using namespace RenderCadenceCompositor;
|
|
|
|
HttpControlServer server;
|
|
HttpControlServer::HttpRequest request;
|
|
request.method = "GET";
|
|
request.path = "/api/nope";
|
|
|
|
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
|
|
ExpectEquals(response.status, "404 Not Found", "unknown endpoint returns 404");
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
TestParsesHttpRequest();
|
|
TestStateEndpointUsesCallback();
|
|
TestWebSocketAcceptKey();
|
|
TestRootServesUiIndex();
|
|
TestKnownPostEndpointReturnsActionError();
|
|
TestLayerPostEndpointsUseCallbacks();
|
|
TestUnknownEndpointReturns404();
|
|
|
|
if (gFailures != 0)
|
|
{
|
|
std::cerr << gFailures << " RenderCadenceCompositorHttpControlServer test failure(s).\n";
|
|
return 1;
|
|
}
|
|
|
|
std::cout << "RenderCadenceCompositorHttpControlServer tests passed.\n";
|
|
return 0;
|
|
}
|