Add Unity Pipeline package support
This commit is contained in:
+187
@@ -0,0 +1,187 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Tests.Runtime;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.ServerLifecyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for simultaneous operation of Editor and Runtime servers.
|
||||
/// Validates port range separation and dual server functionality.
|
||||
///
|
||||
/// Marked [Explicit]: starts a real EditorPipelineServer + RuntimePipelineManager, which
|
||||
/// conflict with the live editor server present in every editor session. Excluded from the
|
||||
/// normal/dogfood run; run deliberately (e.g. Test Runner window). See [[GlobalServerStateGuard]].
|
||||
/// </summary>
|
||||
[Explicit("Server-lifecycle test; conflicts with the live editor server. Run deliberately.")]
|
||||
[Category("ServerLifecycle")]
|
||||
public class DualServerTests
|
||||
{
|
||||
private GameObject m_TestGameObject;
|
||||
private RuntimePipelineManager m_RuntimeManager;
|
||||
private EditorPipelineServer m_EditorServer;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
// RuntimePipelineManager + EditorPipelineServer here mutate global state shared with
|
||||
// the live editor server (descriptor file, command discovery).
|
||||
GlobalServerStateGuard.Capture();
|
||||
|
||||
// Create test GameObject with RuntimePipelineManager
|
||||
m_TestGameObject = new GameObject("TestRuntimePipelineManager");
|
||||
m_RuntimeManager = m_TestGameObject.AddComponent<RuntimePipelineManager>();
|
||||
|
||||
// Configure for testing
|
||||
m_RuntimeManager.enableInBuilds = true;
|
||||
|
||||
// Create Editor server for dual-server tests
|
||||
m_EditorServer = new EditorPipelineServer();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// Stop servers
|
||||
if (m_RuntimeManager != null && m_RuntimeManager.IsServerRunning)
|
||||
{
|
||||
m_RuntimeManager.StopServer();
|
||||
}
|
||||
|
||||
if (m_EditorServer != null && m_EditorServer.IsRunning)
|
||||
{
|
||||
m_EditorServer.Stop();
|
||||
}
|
||||
|
||||
// Clean up GameObjects
|
||||
if (m_TestGameObject != null)
|
||||
{
|
||||
Object.DestroyImmediate(m_TestGameObject);
|
||||
}
|
||||
|
||||
// Restore global state the servers/manager mutated (runs last, after our own cleanup).
|
||||
GlobalServerStateGuard.Restore();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DualServers_EditorAndRuntime_UsesDifferentPortRanges()
|
||||
{
|
||||
// Act - Start Editor server first
|
||||
m_EditorServer.Start();
|
||||
yield return null;
|
||||
|
||||
// Start Runtime server
|
||||
m_RuntimeManager.StartServer();
|
||||
yield return null;
|
||||
|
||||
// Assert - Both servers should be running on different port ranges
|
||||
Assert.IsTrue(m_EditorServer.IsRunning, "Editor server should be running");
|
||||
Assert.IsTrue(m_RuntimeManager.IsServerRunning, "Runtime server should be running");
|
||||
|
||||
// Editor should use 7800-7899
|
||||
Assert.GreaterOrEqual(m_EditorServer.Port, 7800, "Editor server should use port >= 7800");
|
||||
Assert.LessOrEqual(m_EditorServer.Port, 7899, "Editor server should use port <= 7899");
|
||||
|
||||
// Runtime should use 7900-7999
|
||||
Assert.GreaterOrEqual(m_RuntimeManager.ActualPort, 7900, "Runtime server should use port >= 7900");
|
||||
Assert.LessOrEqual(m_RuntimeManager.ActualPort, 7999, "Runtime server should use port <= 7999");
|
||||
|
||||
// Ports should not conflict
|
||||
Assert.AreNotEqual(m_EditorServer.Port, m_RuntimeManager.ActualPort, "Editor and Runtime servers should use different ports");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DualServers_BothStatusEndpoints_ReturnValidResponses()
|
||||
{
|
||||
// Arrange - Start both servers
|
||||
m_EditorServer.Start();
|
||||
m_RuntimeManager.StartServer();
|
||||
yield return null;
|
||||
|
||||
if (!m_EditorServer.IsRunning || !m_RuntimeManager.IsServerRunning)
|
||||
{
|
||||
Assert.Fail("Both servers should be running for this test");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Test Editor server status endpoint
|
||||
PipelineResponse editorResponse = null;
|
||||
using (var editorClient = new PipelineClient(m_EditorServer))
|
||||
yield return editorClient.GetAsync("/api/status", r => editorResponse = r);
|
||||
|
||||
Assert.IsTrue(editorResponse.IsSuccess, $"Editor status endpoint should work: {editorResponse.Error}");
|
||||
Assert.IsNotEmpty(editorResponse.RawResponse, "Editor status response should not be empty");
|
||||
|
||||
// Test Runtime server status endpoint
|
||||
PipelineResponse runtimeResponse = null;
|
||||
using (var runtimeClient = new PipelineClient(m_RuntimeManager))
|
||||
yield return runtimeClient.GetAsync("/api/status", r => runtimeResponse = r);
|
||||
|
||||
Assert.IsTrue(runtimeResponse.IsSuccess, $"Runtime status endpoint should work: {runtimeResponse.Error}");
|
||||
Assert.IsNotEmpty(runtimeResponse.RawResponse, "Runtime status response should not be empty");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DualServers_Authentication_EachRequiresOwnToken()
|
||||
{
|
||||
// Arrange - Start both servers
|
||||
m_EditorServer.Start();
|
||||
m_RuntimeManager.StartServer();
|
||||
yield return null;
|
||||
|
||||
if (!m_EditorServer.IsRunning || !m_RuntimeManager.IsServerRunning)
|
||||
{
|
||||
Assert.Fail("Both servers should be running for this test");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// The runtime client authenticates with the server's own token; a request should be
|
||||
// accepted (not 401). (Rejection of missing/invalid tokens is covered by ServerAuthTests.)
|
||||
PipelineResponse runtimeResponse = null;
|
||||
using (var runtimeClient = new PipelineClient(m_RuntimeManager))
|
||||
yield return runtimeClient.GetAsync("/api/status", r => runtimeResponse = r);
|
||||
|
||||
Assert.AreNotEqual(401, runtimeResponse.StatusCode, "Runtime server should accept its own token");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DualServers_StartStopSequence_WorksCorrectly()
|
||||
{
|
||||
// Test various start/stop sequences to ensure no interference
|
||||
|
||||
// Sequence 1: Start Editor first
|
||||
m_EditorServer.Start();
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
m_RuntimeManager.StartServer();
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
|
||||
Assert.IsTrue(m_EditorServer.IsRunning, "Editor should be running");
|
||||
Assert.IsTrue(m_RuntimeManager.IsServerRunning, "Runtime should be running");
|
||||
|
||||
// Stop both
|
||||
m_EditorServer.Stop();
|
||||
m_RuntimeManager.StopServer();
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
|
||||
Assert.IsFalse(m_EditorServer.IsRunning, "Editor should be stopped");
|
||||
Assert.IsFalse(m_RuntimeManager.IsServerRunning, "Runtime should be stopped");
|
||||
|
||||
// Sequence 2: Start Runtime first
|
||||
m_RuntimeManager.StartServer();
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
m_EditorServer.Start();
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
|
||||
Assert.IsTrue(m_EditorServer.IsRunning, "Editor should be running");
|
||||
Assert.IsTrue(m_RuntimeManager.IsServerRunning, "Runtime should be running");
|
||||
|
||||
// Should still use correct port ranges regardless of start order
|
||||
Assert.GreaterOrEqual(m_EditorServer.Port, 7800, "Editor should use Editor range");
|
||||
Assert.LessOrEqual(m_EditorServer.Port, 7899, "Editor should use Editor range");
|
||||
Assert.GreaterOrEqual(m_RuntimeManager.ActualPort, 7900, "Runtime should use Runtime range");
|
||||
Assert.LessOrEqual(m_RuntimeManager.ActualPort, 7999, "Runtime should use Runtime range");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb4e86ff6994ad141ae2cc0f129206e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Unity.Pipeline.Commands;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using Unity.Pipeline.Tests.Editor;
|
||||
using Unity.Pipeline.Tests.Runtime;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.ServerLifecyle
|
||||
{
|
||||
/// <summary>
|
||||
/// End-to-end tests demonstrating complete Pipeline workflow.
|
||||
/// These test the full roundtrip: discovery → connection → command execution
|
||||
/// that simulates real CLI usage patterns.
|
||||
///
|
||||
/// Marked [Explicit]: starts its own EditorPipelineServer (and manages the shared descriptor),
|
||||
/// which conflicts with the live editor server. Excluded from the normal/dogfood run.
|
||||
/// TODO: to rejoin the dogfood run, refactor to drive an isolated TestEditorPipelineServer
|
||||
/// (ports 7850+, WritesDescriptor=false) instead of starting a real server.
|
||||
/// </summary>
|
||||
[Explicit("Starts its own server; conflicts with the live editor server. Run deliberately.")]
|
||||
[Category("ServerLifecycle")]
|
||||
public class EndToEndTests
|
||||
{
|
||||
[Test]
|
||||
public async Task CompleteWorkflow_DiscoverConnectExecute_WorksEndToEnd()
|
||||
{
|
||||
// Setup
|
||||
CommandRegistry.SetDiscovery(new TypeCacheCommandDiscovery());
|
||||
|
||||
// Step 1: Start Pipeline Server (simulates Editor startup)
|
||||
var server = new EditorPipelineServer();
|
||||
server.Start();
|
||||
|
||||
try
|
||||
{
|
||||
var projectPath = Path.GetDirectoryName(Application.dataPath);
|
||||
|
||||
// Step 2: CLI Discovery - Find running instances
|
||||
var instances = MockCliDiscovery.DiscoverInstances(new[] { projectPath });
|
||||
var runningInstances = instances.Where(i => i.IsRunning).ToList();
|
||||
|
||||
Assert.Greater(runningInstances.Count, 0, "Should discover at least one running instance");
|
||||
|
||||
var targetInstance = runningInstances.First();
|
||||
|
||||
// Give server a moment to fully start
|
||||
await Task.Delay(100);
|
||||
|
||||
// Step 3: CLI Connection Validation
|
||||
var isReachable = await MockCliDiscovery.ValidateConnection(targetInstance.Descriptor);
|
||||
Assert.IsTrue(isReachable, "Discovered instance should be reachable");
|
||||
|
||||
// Step 4: CLI Command Discovery - Get available commands
|
||||
using (var client = new PipelineClient(server))
|
||||
{
|
||||
var commandsResponse = await client.GetAsync("/api/commands");
|
||||
Assert.IsTrue(commandsResponse.IsSuccess, "Should be able to get commands list");
|
||||
|
||||
var commands = commandsResponse.JsonResponse["commands"] as JArray;
|
||||
Assert.Greater(commands.Count, 0, "Should have available commands");
|
||||
|
||||
// Step 5: CLI Command Execution - Execute log_editor command
|
||||
var execResponse = await client.ExecuteCommandAsync(
|
||||
"log_editor", new { message = "Hello from end-to-end test!" });
|
||||
|
||||
Assert.IsTrue(execResponse.IsSuccess,
|
||||
$"Command execution should succeed. Response: {execResponse.RawResponse}");
|
||||
|
||||
var execData = execResponse.JsonResponse;
|
||||
Assert.IsTrue(execData["success"].ToObject<bool>(), "Command should execute successfully");
|
||||
Assert.AreEqual("log_editor", execData["command"]?.ToString());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RealWorldScenario_MultipleCommands_ExecutesSequentially()
|
||||
{
|
||||
// Setup
|
||||
CommandRegistry.SetDiscovery(new TypeCacheCommandDiscovery());
|
||||
|
||||
var server = new EditorPipelineServer();
|
||||
server.Start();
|
||||
|
||||
try
|
||||
{
|
||||
using (var client = new PipelineClient(server))
|
||||
{
|
||||
// Simulate CLI batch execution of multiple commands
|
||||
var commands = new CommandExecutionRequest[]
|
||||
{
|
||||
new CommandExecutionRequest("log_editor") { Parameters = { ["message"] = "Starting batch execution" } },
|
||||
new CommandExecutionRequest("test_types") { Parameters = { ["count"] = 5, ["enabled"] = true, ["factor"] = 2.5f } },
|
||||
new CommandExecutionRequest("log_editor") { Parameters = { ["message"] = "Batch execution completed" } }
|
||||
};
|
||||
|
||||
foreach (var cmd in commands)
|
||||
{
|
||||
var response = await client.ExecuteCommandAsync(cmd.Command, cmd.Parameters);
|
||||
|
||||
Assert.IsTrue(response.IsSuccess,
|
||||
$"Command '{cmd.Command}' should execute successfully. Response: {response.RawResponse}");
|
||||
|
||||
Assert.IsTrue(response.JsonResponse["success"].ToObject<bool>(),
|
||||
$"Command '{cmd.Command}' should succeed");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a97f33bd666ad6340965b07f15094045
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
using System.IO;
|
||||
using Unity.Pipeline.Commands;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Models;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.ServerLifecyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Isolates server-lifecycle tests from the live editor pipeline server, which shares some global
|
||||
/// state in-process. Call Capture() in [SetUp] and Restore() in [TearDown].
|
||||
///
|
||||
/// It records whether a live server was advertised before the test and restores command discovery
|
||||
/// (RuntimePipelineManager.Awake switches it to reflection). The main-thread dispatcher is
|
||||
/// per-server now, so there is nothing global to swap.
|
||||
/// </summary>
|
||||
public static class GlobalServerStateGuard
|
||||
{
|
||||
static string s_DescriptorPath;
|
||||
static bool s_HadLiveServer;
|
||||
|
||||
public static void Capture()
|
||||
{
|
||||
var projectPath = Path.GetDirectoryName(Application.dataPath);
|
||||
s_DescriptorPath = InstanceDescriptor.GetDescriptorFilePath(projectPath);
|
||||
// Key off the live server OBJECT, not the descriptor file: a prior test may have left the
|
||||
// live server running while the shared descriptor was already deleted (object/file desync).
|
||||
// Keying off the file would then wrongly skip the restart below.
|
||||
s_HadLiveServer = PipelineServerStartup.Server != null;
|
||||
}
|
||||
|
||||
public static void Restore()
|
||||
{
|
||||
// A server-lifecycle test starts/stops its own EditorPipelineServer on the live port,
|
||||
// which tears down the live listener and deletes the shared `.unity-pipeline-port`
|
||||
// descriptor. Rewriting the descriptor file is NOT enough — it would advertise a port
|
||||
// nothing listens on (the symptom: menu shows "running" but the server is unreachable).
|
||||
// Fully restart the live server so the listener and descriptor are consistent (this also
|
||||
// re-arms the watchdog and auto-tick). Mirrors RuntimePipelineServerTests.TearDown.
|
||||
try
|
||||
{
|
||||
if (s_HadLiveServer)
|
||||
PipelineServerStartup.RestartServer();
|
||||
else if (s_DescriptorPath != null && File.Exists(s_DescriptorPath))
|
||||
File.Delete(s_DescriptorPath); // No live server before the test — clear any stray descriptor.
|
||||
}
|
||||
catch { /* best effort */ }
|
||||
|
||||
// RuntimePipelineManager.Awake switches discovery to reflection; restore TypeCache.
|
||||
CommandRegistry.SetDiscovery(new TypeCacheCommandDiscovery());
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a715663d547625346836df81c2aee9a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Models;
|
||||
using Unity.Pipeline.Tests.Runtime;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.ServerLifecyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression test for the full-suite run leaving the live editor server unreachable.
|
||||
///
|
||||
/// <see cref="GlobalServerStateGuard.Restore"/> is the safety net that server-lifecycle tests
|
||||
/// rely on (in [TearDown]) to hand the live editor server back intact. Its contract is not "the
|
||||
/// descriptor file is present" but "the live server is actually running and reachable". A guard
|
||||
/// that only rewrites the descriptor file leaves it advertising a port nothing listens on —
|
||||
/// which is exactly how the suite used to finish with the server "running" in the menu but
|
||||
/// unreachable.
|
||||
/// </summary>
|
||||
[Explicit("Manipulates the live editor server; run deliberately (e.g. from the Test Runner window).")]
|
||||
[Category("ServerLifecycle")]
|
||||
public class GlobalServerStateGuardTests
|
||||
{
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// Safety net: never leave the live server down for the next test/dogfood command, even if
|
||||
// an assertion above failed before the guard could restore it.
|
||||
PipelineServerStartup.EnsureServerStarted();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Restore_AfterLiveListenerTornDown_LeavesServerReachable()
|
||||
{
|
||||
// Arrange: a live server is running and advertised (as in every interactive editor).
|
||||
PipelineServerStartup.EnsureServerStarted();
|
||||
Assert.IsNotNull(PipelineServerStartup.Server, "Precondition: a live server should exist");
|
||||
Assert.IsTrue(PipelineServerStartup.Server.IsRunning, "Precondition: live server should be running");
|
||||
|
||||
GlobalServerStateGuard.Capture();
|
||||
|
||||
// Simulate a server-lifecycle test that tears down the live listener and deletes the
|
||||
// shared descriptor (what new EditorPipelineServer().Start()/Stop() does on the live port).
|
||||
PipelineServerStartup.StopServer();
|
||||
var projectRoot = Path.GetDirectoryName(Application.dataPath);
|
||||
Assert.IsNull(InstanceDescriptor.ReadFromProjectRoot(projectRoot),
|
||||
"Sanity: descriptor should be gone after the live server stopped");
|
||||
|
||||
// Act
|
||||
GlobalServerStateGuard.Restore();
|
||||
|
||||
// Assert: the guard must leave a live, reachable server — not just a descriptor file.
|
||||
var server = PipelineServerStartup.Server;
|
||||
Assert.IsNotNull(server, "Restore() must leave a live server running");
|
||||
Assert.IsTrue(server.IsRunning, "Restore() must leave the live server running");
|
||||
|
||||
var descriptor = InstanceDescriptor.ReadFromProjectRoot(projectRoot);
|
||||
Assert.IsNotNull(descriptor, "Restore() must leave a discovery descriptor");
|
||||
Assert.AreEqual(server.Port, descriptor.Port,
|
||||
"Descriptor port must match the running server's port");
|
||||
|
||||
using (var client = new PipelineClient($"http://localhost:{descriptor.Port}", descriptor.EvalToken))
|
||||
{
|
||||
var status = await client.GetStatusAsync();
|
||||
Assert.IsTrue(status.IsSuccess,
|
||||
"Live server must answer /api/status after Restore() — the descriptor must point at a live listener");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c16e03783027c2a48bf19735c2b8823d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Models;
|
||||
using Unity.Pipeline.Tests.Runtime;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.ServerLifecyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for PipelineServer lifecycle and basic HTTP functionality.
|
||||
/// These test the public interface behaviors that CLI tools will rely on.
|
||||
///
|
||||
/// Marked [Explicit]: they start/stop a real EditorPipelineServer and manage the shared
|
||||
/// `.unity-pipeline-port` descriptor, which conflicts with the live editor server that exists
|
||||
/// in every editor session. They are excluded from the normal/dogfood run and meant to be run
|
||||
/// deliberately (e.g. from the Test Runner window). See [[GlobalServerStateGuard]].
|
||||
/// </summary>
|
||||
[Explicit("Server-lifecycle test; conflicts with the live editor server. Run deliberately.")]
|
||||
[Category("ServerLifecycle")]
|
||||
public class PipelineServerTests
|
||||
{
|
||||
// These tests start/stop their own EditorPipelineServer, which writes/deletes the shared
|
||||
// `.unity-pipeline-port` descriptor and would corrupt the live editor server. Guard it.
|
||||
[SetUp]
|
||||
public void SetUp() => GlobalServerStateGuard.Capture();
|
||||
|
||||
[TearDown]
|
||||
public void TearDown() => GlobalServerStateGuard.Restore();
|
||||
|
||||
[Test]
|
||||
public void ServerLifecycle_StartAndStop_ManagesRunningState()
|
||||
{
|
||||
// Arrange
|
||||
var server = new EditorPipelineServer();
|
||||
|
||||
// Assert initial state
|
||||
Assert.IsFalse(server.IsRunning, "Server should not be running initially");
|
||||
Assert.AreEqual(0, server.Port, "Port should be 0 when not running");
|
||||
|
||||
// Act - Start server
|
||||
server.Start();
|
||||
|
||||
// Assert - Started state
|
||||
Assert.IsTrue(server.IsRunning, "Server should be running after Start()");
|
||||
Assert.Greater(server.Port, 0, "Port should be assigned after Start()");
|
||||
Assert.GreaterOrEqual(server.Port, 7800, "Port should be in range 7800-7899");
|
||||
Assert.LessOrEqual(server.Port, 7899, "Port should be in range 7800-7899");
|
||||
|
||||
// Act - Stop server
|
||||
server.Stop();
|
||||
|
||||
// Assert - Stopped state
|
||||
Assert.IsFalse(server.IsRunning, "Server should not be running after Stop()");
|
||||
// Note: Port may retain value after stop for diagnostic purposes
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task StatusEndpoint_GetApiStatus_ReturnsBasicServerInfo()
|
||||
{
|
||||
// Arrange
|
||||
var server = new EditorPipelineServer();
|
||||
server.Start();
|
||||
|
||||
try
|
||||
{
|
||||
using (var client = new PipelineClient(server))
|
||||
{
|
||||
// Act - Call /api/status endpoint (basic server health)
|
||||
var response = await client.GetHttpAsync("/api/status");
|
||||
var jsonContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Assert - Response structure
|
||||
Assert.IsTrue(response.IsSuccessStatusCode,
|
||||
$"Basic status endpoint should return success, got: {response.StatusCode}");
|
||||
Assert.AreEqual("application/json", response.Content.Headers.ContentType.MediaType,
|
||||
"Status endpoint should return JSON content type");
|
||||
|
||||
// Assert - Basic server status JSON structure (not StatusResponse)
|
||||
var statusJson = JObject.Parse(jsonContent);
|
||||
Assert.IsNotNull(statusJson, "Should be able to parse basic status JSON");
|
||||
|
||||
// Verify basic server fields
|
||||
Assert.AreEqual("ready", statusJson["status"]?.ToString(), "Server status should be ready");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InstanceDescriptor_ServerStartStop_ManagesDescriptorFile()
|
||||
{
|
||||
// Arrange
|
||||
var server = new EditorPipelineServer();
|
||||
var projectPath = Path.GetDirectoryName(Application.dataPath);
|
||||
var descriptorFilePath = InstanceDescriptor.GetDescriptorFilePath(projectPath);
|
||||
|
||||
// Ensure no existing descriptor file
|
||||
if (File.Exists(descriptorFilePath))
|
||||
{
|
||||
File.Delete(descriptorFilePath);
|
||||
}
|
||||
|
||||
// Act - Start server
|
||||
server.Start();
|
||||
|
||||
// Assert - Descriptor file created
|
||||
Assert.IsTrue(File.Exists(descriptorFilePath),
|
||||
"Instance descriptor file should be created when server starts");
|
||||
|
||||
// Read and verify descriptor content
|
||||
var descriptor = InstanceDescriptor.ReadFromProjectRoot(projectPath);
|
||||
Assert.IsNotNull(descriptor, "Should be able to read descriptor file");
|
||||
Assert.AreEqual(server.Port, descriptor.Port, "Descriptor should contain correct port");
|
||||
Assert.Greater(descriptor.Pid, 0, "Descriptor should contain valid PID");
|
||||
Assert.IsNotEmpty(descriptor.ProjectPath, "Descriptor should contain project path");
|
||||
|
||||
// Act - Stop server
|
||||
server.Stop();
|
||||
|
||||
// Assert - Descriptor file removed
|
||||
Assert.IsFalse(File.Exists(descriptorFilePath),
|
||||
"Instance descriptor file should be removed when server stops");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42319be0b075849479314f25fe300c69
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user