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

114 lines
5.6 KiB
C#
Raw Normal View History

2026-08-04 03:10:06 -07:00
using System.Text.Json;
using NucleicApp.Services;
using NucleicApp.ViewModels;
using NucleicProtocol.Interop;
namespace NucleicApp.Tests;
public sealed class PresentationModelTests
{
[Fact]
public void HomeDerivesAttentionActivityAndCountsOnlyFromHostProjections()
{
using var store = new RendererStore();
store.Apply("""
{"input":[
{"archived":false,"disposition":"awaitingInput","pendingApprovalCount":0,
"sessionID":"needs-input","status":"awaitingInput","title":"Needs input","updatedAt":3},
{"archived":false,"pendingApprovalCount":0,
"sessionID":"running","status":"running","title":"Running","updatedAt":2},
{"archived":true,"pendingApprovalCount":1,
"sessionID":"archived","status":"awaitingApproval","title":"Archived","updatedAt":4}
],"t":"sessionList"}
""");
store.Apply("""
{"activity":[],"counts":{"activeChats":2,"chats":3,"messages":8,
"projects":1,"tokens":1200},"projects":[],"statusFeeds":[],
"t":"dashboard","todos":[{"status":"open"},{"status":"done"}]}
""");
using var home = new HomeViewModel(
store, () => new DateTimeOffset(2026, 8, 4, 9, 0, 0, TimeSpan.Zero));
Assert.Equal("Good morning", home.Greeting);
Assert.Equal(new DashboardCounts(1, 2, 3, 8, 1200), home.Counts);
Assert.Equal("2 active of 3 chats · 1 project", home.Subtitle);
Assert.Equal("needs-input", Assert.Single(home.NeedsAttention).Id);
Assert.Equal("running", Assert.Single(home.InProgress).Id);
Assert.Equal(2, home.LiveSessions.Count);
Assert.Equal(1, home.OpenTodoCount);
}
[Fact]
public async Task ShellSessionLifecycleEmitsSubscriptionComposerAndApprovalIntents()
{
var directory = Directory.CreateTempSubdirectory("nucleic-shell-");
try
{
var rendezvousPath = Path.Combine(directory.FullName, "hostd.json");
await File.WriteAllTextAsync(rendezvousPath, JsonSerializer.Serialize(
ProtocolInteropTests.ValidRendezvous()));
var native = new FakeNativeProtocol
{
ProjectionResult =
"{\"sessionID\":\"session-1\",\"splices\":[{\"deleteCount\":0,\"items\":[],\"start\":0}],\"version\":1}",
};
using var store = new RendererStore(new ProtocolProjection(native));
store.Apply(TestRepository.Fixture("host", "sessionList"));
store.Apply(TestRepository.Fixture("host", "approvalRequested"));
var client = new FakeProtocolClient();
await using var connection = new HostConnection(
store, Path.Combine(directory.FullName, "identity"),
new FakeProtocolClientFactory(client), InlineRendererDispatcher.Instance);
await connection.StartLocalAsync(rendezvousPath);
using var shell = new ShellViewModel(store, connection);
Assert.Equal(ProtocolResult.Ok, shell.OpenSession("session-1", 6));
var detail = Assert.IsType<SessionDetailViewModel>(shell.SelectedSession);
Assert.Equal(ShellSection.Sessions, shell.Section);
Assert.Equal("session-1", detail.Session?.Id);
Assert.Equal("approval-1", Assert.Single(detail.PendingApprovals).Id);
Assert.Equal(ProtocolResult.InvalidArgument, detail.SendText(" "));
Assert.Equal(ProtocolResult.Ok, detail.SendText("continue"));
var approval = Assert.Single(detail.PendingApprovals);
Assert.Equal(ProtocolResult.Ok, detail.Allow(approval));
Assert.Equal(ProtocolResult.Ok, detail.Deny(approval, "not now"));
Assert.Equal(ProtocolResult.Ok,
detail.AllowAlways(approval, ApprovalAlwaysScope.ToolName));
Assert.Equal(ProtocolResult.Ok, detail.CancelRun(approval));
store.Apply(TestRepository.Fixture("host", "pong"));
Assert.NotNull(detail.LastProjection);
shell.Navigate(ShellSection.Home);
Assert.Null(shell.SelectedSession);
Assert.Equal(ShellSection.Home, shell.Section);
var messages = client.Intents.Select(ProtocolMessage.Parse).ToArray();
Assert.Equal(
["subscribe", "sendInput", "approvalRespond", "approvalRespond",
"approvalRespond", "approvalRespond", "unsubscribe"],
messages.Select(message => message.Tag));
Assert.Equal(6UL, messages[0].Root.GetProperty("sinceSeq").GetUInt64());
Assert.Equal("continue",
messages[1].Root.GetProperty("input").GetProperty("parts")[0]
.GetProperty("text").GetString());
Assert.Equal("allow",
messages[2].Root.GetProperty("decision").GetProperty("type").GetString());
Assert.Equal("deny",
messages[3].Root.GetProperty("decision").GetProperty("type").GetString());
Assert.Equal("not now",
messages[3].Root.GetProperty("decision").GetProperty("reason").GetString());
Assert.Equal("allowAlways",
messages[4].Root.GetProperty("decision").GetProperty("type").GetString());
Assert.Equal("toolName",
messages[4].Root.GetProperty("decision").GetProperty("scope").GetString());
Assert.Equal("cancelRun",
messages[5].Root.GetProperty("decision").GetProperty("type").GetString());
Assert.Equal("session-1", messages[6].Root.GetProperty("sessionID").GetString());
}
finally { directory.Delete(recursive: true); }
}
}