Tests architecture
How the package's tests exercise commands. There are two complementary styles — ViaClient (over real HTTP, end-to-end) and CommandDirect (calling the static command method straight) — backed by a small set of shared test helpers.
Two test styles
ViaClient — exercise the full HTTP path
Spin up an isolated PipelineTestServer and call server.Execute(command, params). This goes over HTTP through the same routing, parameter coercion, and threading the real server uses — it tests the command as a client sees it.
[Test]
public void SetTransform_ViaClient_ArrayParams_Apply()
{
var go = Track(new GameObject("Xform219_Arrays"));
using (var server = new PipelineTestServer())
{
var response = server.Execute("set_transform", new
{
target = new { instanceId = PipelineUtils.GetObjectId(go) },
position = new[] { 1f, 2f, 3f },
rotation = new[] { 0f, 90f, 0f },
scale = new[] { 2f, 2f, 2f }
});
Assert.IsTrue(response.IsSuccess, $"set_transform should succeed: {response.Error}");
Assert.AreEqual(new Vector3(1, 2, 3), go.transform.localPosition);
}
}
The test server is isolated: it uses the test editor port range 7850–7899 and writes no descriptor, so it never disturbs the live editor server (which uses 7800–7849). Execute pumps the server's own dispatcher while the HTTP call is in flight, so MainThreadRequired commands complete even from a plain [Test] that blocks the main thread — no deadlock.
CommandDirect — call the static method directly
Call the command's static method with typed arguments. This is synchronous, with no server, HTTP, or dispatcher involved — fast and ideal for unit-testing command logic and parameter handling.
[Test]
public void SetTransform_Direct_TypedArrays_Apply()
{
var go = Track(new GameObject("Xform219_Direct"));
GameObjectCommands.SetTransform(RefTo(go),
position: new[] { 1f, 2f, 3f },
rotation: new[] { 0f, 90f, 0f },
scale: new[] { 2f, 2f, 2f });
Assert.AreEqual(new Vector3(1, 2, 3), go.transform.localPosition);
Assert.AreEqual(new Vector3(2, 2, 2), go.transform.localScale);
}
Use CommandDirect to verify a command's behavior with strongly-typed inputs; use ViaClient to additionally verify wire-level concerns (JSON coercion, required-parameter validation, threading).
Shared helpers
| Helper | Role |
|---|---|
PipelineTestServer |
Disposable wrapper that starts an isolated TestEditorPipelineServer, wires up a PipelineClient, and exposes Execute(command, parameters, timeoutMs = 30000). Pumps the server dispatcher during each call to avoid main-thread deadlock. |
TestEditorPipelineServer |
EditorPipelineServer subclass for tests. Overrides WritesDescriptor => false, GetPortRange() => (7850, 7899), and GetToken() (from SecurityTokenManager) so it is fully isolated from the live server. |
PipelineClient |
Test-side HTTP client. Constructed from a URL+token or a server/manager instance. Key methods: ExecuteCommandAsync (/api/exec), GetStatusAsync (/api/status), PostJsonAsync. Returns a PipelineResponse (IsSuccess, StatusCode, Error, JsonResponse, IsCommandSuccess). |
EditorTestUtilities |
Async helpers for editor state, e.g. WaitFor(Func<bool> condition, timeoutMs) to poll a condition without busy-waiting. |
LiveServerGuard |
Assembly-level ITestAction that asserts the live editor server survives the test run. |
PipelineTestServer setup
public PipelineTestServer()
{
m_Server = new TestEditorPipelineServer();
m_Server.Start(); // auto-assigns a port in 7850-7899; writes no descriptor
m_Client = new PipelineClient($"http://localhost:{m_Server.Port}", SecurityTokenManager.GetOrCreateToken());
}
Wrap it in a using (it is disposable) so the isolated server is stopped at the end of the test.
LiveServerGuard
Applied once at assembly scope ([assembly: LiveServerGuard]), it runs before and after every test in the editor suite. If a live editor server was advertising its descriptor before a test, the guard asserts afterwards that the test did not disturb it — the descriptor still exists, the port is unchanged, and the server is still listening:
Assert.IsNotNull(after, $"'{test.Name}' deleted the live pipeline server descriptor");
Assert.AreEqual(m_Before.Port, after.Port, $"'{test.Name}' changed the port");
Assert.IsTrue(IsListening(after.Port, after.EvalToken), $"'{test.Name}' left the server not responding");
This is what makes it safe to "dogfood" — run the test suite against the very editor you are driving — without a test clobbering the live server. Tests that intentionally start/stop the live server are marked [Explicit] and must hand it back intact.
See also
- Creating commands — the command API these tests exercise.
- Connectivity — ports, descriptor, and auth (the test server deliberately skips the descriptor).