Files
nucleic-windows/NucleicApp.Tests/RendererCoreTests.cs
T

121 lines
5.1 KiB
C#
Raw Normal View History

2026-08-04 02:46:53 -07:00
using System.Net;
using System.Net.Sockets;
using System.Text.Json;
using NucleicApp.Services;
using NucleicProtocol.Interop;
namespace NucleicApp.Tests;
public sealed class RendererCoreTests
{
[Fact]
public void StoreProjectsSessionsApprovalsAndSettingsFromHostMessagesOnly()
{
using var store = new RendererStore();
store.Apply(TestRepository.Fixture("host", "sessionList"));
store.Apply(TestRepository.Fixture("host", "approvalRequested"));
store.Apply(TestRepository.Fixture("host", "dashboard"));
store.Apply(TestRepository.Fixture("host", "settings"));
var session = Assert.Single(store.Sessions);
Assert.Equal("session-1", session.Id);
Assert.Equal("awaitingApproval", session.Status);
Assert.Equal(1, session.PendingApprovalCount);
Assert.Equal("approval-1", Assert.Single(store.PendingApprovals).Id);
Assert.NotNull(store.Dashboard);
Assert.True(store.Settings!.Value.GetProperty("resolveApprovalsViaCloud").GetBoolean());
store.Apply(TestRepository.Fixture("host", "approvalResolved"));
Assert.Empty(store.PendingApprovals);
}
[Fact]
public async Task HostConnectionReadsRendezvousAndRequestsInitialStateOnReady()
{
var directory = Directory.CreateTempSubdirectory("nucleic-renderer-");
try
{
var rendezvousPath = Path.Combine(directory.FullName, "hostd.json");
await File.WriteAllTextAsync(rendezvousPath, JsonSerializer.Serialize(
ProtocolInteropTests.ValidRendezvous()));
using var store = new RendererStore();
var client = new FakeProtocolClient();
var factory = new FakeProtocolClientFactory(client);
await using var connection = new HostConnection(
store, Path.Combine(directory.FullName, "identity"), factory,
InlineRendererDispatcher.Instance);
await connection.StartLocalAsync(rendezvousPath);
Assert.Equal("host-1", client.Rendezvous?.HostId);
client.EmitState("{\"state\":\"ready\",\"hostID\":\"host-1\",\"welcome\":{}}");
Assert.Equal(HostConnectionState.Connected, connection.State);
var tags = client.Intents.Select(json => ProtocolMessage.Parse(json).Tag).ToArray();
Assert.Equal(["listSessions", "listDashboard", "listPeers"], tags);
client.EmitMessage(TestRepository.Fixture("host", "sessionUpdated"));
Assert.Equal("session-1", Assert.Single(store.Sessions).Id);
}
finally { directory.Delete(recursive: true); }
}
[Fact]
public async Task HostConnectionKeepsRetryingAfterSynchronousAdmissionFailures()
{
var directory = Directory.CreateTempSubdirectory("nucleic-reconnect-");
try
{
var rendezvousPath = Path.Combine(directory.FullName, "hostd.json");
await File.WriteAllTextAsync(rendezvousPath, JsonSerializer.Serialize(
ProtocolInteropTests.ValidRendezvous()));
using var store = new RendererStore();
var client = new FakeProtocolClient();
client.ConnectLocalResults.Enqueue(ProtocolResult.InvalidState);
client.ConnectLocalResults.Enqueue(ProtocolResult.InvalidState);
client.ConnectLocalResults.Enqueue(ProtocolResult.Ok);
await using var connection = new HostConnection(
store, Path.Combine(directory.FullName, "identity"),
new FakeProtocolClientFactory(client), InlineRendererDispatcher.Instance);
await connection.StartLocalAsync(rendezvousPath);
await WaitUntilAsync(() => client.ConnectLocalCount == 3, TimeSpan.FromSeconds(3));
Assert.Equal(3, client.ConnectLocalCount);
Assert.Equal(HostConnectionState.Reconnecting, connection.State);
}
finally { directory.Delete(recursive: true); }
}
[Fact]
public async Task LauncherAcceptsOnlyALiveLoopbackRendezvous()
{
var directory = Directory.CreateTempSubdirectory("nucleic-launcher-");
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
try
{
var record = ProtocolInteropTests.ValidRendezvous() with
{
Port = ((IPEndPoint)listener.LocalEndpoint).Port,
};
var path = Path.Combine(directory.FullName, "hostd.json");
await File.WriteAllTextAsync(path, JsonSerializer.Serialize(record));
var launcher = new HostdLauncher(new HostdLaunchOptions(
Path.Combine(directory.FullName, "must-not-launch.exe"), path));
var result = await launcher.EnsureRunningAsync();
Assert.Equal(record.Port, result.Port);
}
finally
{
listener.Stop();
directory.Delete(recursive: true);
}
}
private static async Task WaitUntilAsync(Func<bool> condition, TimeSpan timeout)
{
using var deadline = new CancellationTokenSource(timeout);
while (!condition()) await Task.Delay(10, deadline.Token);
}
}