Files
meeting-assistant/MeetingAssistant.Tests/MacOsUserPromptTests.cs
T
dh 728e66dd72
PR and Push Build/Test / windows-build-and-test (push) Failing after 38m25s
PR and Push Build/Test / portable-build-and-test (push) Successful in 8m9s
PR and Push Build/Test / macos-native-full (push) Skipped
ci: consolidate platform pipelines and test prerequisites on macOS support
2026-10-06 09:02:19 +02:00

130 lines
6.8 KiB
C#

using MeetingAssistant.Calendar;
using MeetingAssistant.MacOs;
using MeetingAssistant.Notifications;
using MeetingAssistant.Recording;
using Microsoft.Extensions.Logging.Abstractions;
namespace MeetingAssistant.Tests;
public sealed class MacOsUserPromptTests
{
[Theory]
[InlineData("yes", true)]
[InlineData("no", false)]
public async Task ApprovalWaitsForAnExplicitDesktopDecision(string action, bool approved)
{
using var lifetime = new TestApplicationLifetime();
using var store = CreateStore(lifetime);
IAgentApprovalPromptService prompts = new MacOsAgentApprovalPromptService(store);
var decision = prompts.ConfirmAsync(new AgentApprovalPrompt("Project requested", "Associate Alpha?",
TimeSpan.FromMinutes(10)), CancellationToken.None);
Assert.False(decision.IsCompleted);
var request = Assert.Single(store.List());
Assert.Equal("agent-approval", request.Kind);
Assert.Equal(["yes", "no"], request.Actions.Select(item => item.Id));
Assert.Equal(DesktopUserRequestRespondResult.Accepted, await store.RespondAsync(request.Id, action));
Assert.Equal(approved, await decision);
Assert.Empty(store.List());
}
[Fact]
public async Task InactivityPromptReturnsImmediatelyAndOffersPauseWithOriginLifetime()
{
using var lifetime = new TestApplicationLifetime();
using var origin = new CancellationTokenSource();
var clock = new ManualDesktopRequestTimeProvider();
using var store = new DesktopUserRequestStore(clock, lifetime, NullLogger<DesktopUserRequestStore>.Instance);
IMeetingInactivityPromptService prompts = new MacOsMeetingInactivityPromptService(store);
MeetingInactivityPromptResponse? received = null;
CancellationToken receivedToken = default;
var showing = prompts.ShowStopPromptAsync(
new MeetingInactivityPromptRequest(TimeSpan.FromMinutes(2), clock.GetUtcNow(), 1, TimeSpan.FromMinutes(2)),
(response, token) =>
{
received = response;
receivedToken = token;
return Task.CompletedTask;
}, origin.Token);
Assert.True(showing.IsCompletedSuccessfully);
var request = Assert.Single(store.List());
Assert.Equal("meeting-inactivity", request.Kind);
Assert.Equal(["stop", "continue", "pause"], request.Actions.Select(item => item.Id));
Assert.Equal(DateTimeOffset.Parse("2026-10-03T10:01:00Z"), request.ExpiresAt);
Assert.Equal(DesktopUserRequestRespondResult.Accepted, await store.RespondAsync(request.Id, "pause"));
Assert.Equal(MeetingInactivityPromptResponse.Pause, received);
Assert.Equal(origin.Token, receivedToken);
}
[Theory]
[InlineData(true, "attach-metadata", MeetingStartPromptResponse.AttachMetadataToCurrentMeeting)]
[InlineData(false, "skip", MeetingStartPromptResponse.Skip)]
public async Task CalendarPromptReturnsImmediatelyAndPreservesOfferedActions(bool canAttach, string action, MeetingStartPromptResponse expected)
{
using var lifetime = new TestApplicationLifetime();
var clock = new ManualDesktopRequestTimeProvider();
using var store = new DesktopUserRequestStore(clock, lifetime, NullLogger<DesktopUserRequestStore>.Instance);
IMeetingStartPromptService prompts = new MacOsMeetingStartPromptService(store, clock);
var meeting = new CalendarMeeting("event-1", "Alpha planning", clock.GetUtcNow(), clock.GetUtcNow().AddMinutes(2));
MeetingStartPromptResponse? received = null;
var showing = prompts.ShowPromptAsync(new MeetingStartPromptRequest(meeting, canAttach),
(response, _) => { received = response; return Task.CompletedTask; }, CancellationToken.None);
Assert.True(showing.IsCompletedSuccessfully);
var request = Assert.Single(store.List());
Assert.Equal("meeting-start", request.Kind);
Assert.Contains("Alpha planning", request.Message);
Assert.Equal(DateTimeOffset.Parse("2026-10-03T10:02:00Z"), request.ExpiresAt);
Assert.Equal(canAttach, request.Actions.Any(item => item.Id == "attach-metadata"));
Assert.Equal(DesktopUserRequestRespondResult.Accepted, await store.RespondAsync(request.Id, action));
Assert.Equal(expected, received);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task ApprovalExpirationOrOriginCancellationNeverGrantsConsent(bool cancelOrigin)
{
using var lifetime = new TestApplicationLifetime();
using var origin = new CancellationTokenSource();
var clock = new ManualDesktopRequestTimeProvider();
using var store = new DesktopUserRequestStore(clock, lifetime, NullLogger<DesktopUserRequestStore>.Instance);
IAgentApprovalPromptService prompts = new MacOsAgentApprovalPromptService(store);
var decision = prompts.ConfirmAsync(new AgentApprovalPrompt("Workflow requested", "Update matching rule?",
TimeSpan.FromMinutes(10)), origin.Token);
var request = Assert.Single(store.List());
if (cancelOrigin)
{
await origin.CancelAsync();
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => decision);
}
else
{
clock.Advance(TimeSpan.FromMinutes(10));
Assert.False(await decision);
}
Assert.Empty(store.List());
Assert.Equal(DesktopUserRequestRespondResult.NoLongerPending, await store.RespondAsync(request.Id, "yes"));
}
[Fact]
public async Task DismissingInactivityLeavesUnrelatedApprovalPending()
{
using var lifetime = new TestApplicationLifetime();
using var store = CreateStore(lifetime);
IAgentApprovalPromptService approvals = new MacOsAgentApprovalPromptService(store);
var decision = approvals.ConfirmAsync(new AgentApprovalPrompt("Project requested", "Associate Alpha?",
TimeSpan.FromMinutes(10)), CancellationToken.None);
var approval = Assert.Single(store.List());
IMeetingInactivityPromptService inactivity = new MacOsMeetingInactivityPromptService(store);
await inactivity.ShowStopPromptAsync(new MeetingInactivityPromptRequest(TimeSpan.FromMinutes(2),
DateTimeOffset.UtcNow, 1, TimeSpan.FromMinutes(2)), (_, _) => Task.CompletedTask, CancellationToken.None);
Assert.Equal(2, store.List().Count);
await inactivity.DismissAllAsync(CancellationToken.None);
Assert.Equal(approval.Id, Assert.Single(store.List()).Id);
await store.RespondAsync(approval.Id, "no");
Assert.False(await decision);
}
private static DesktopUserRequestStore CreateStore(TestApplicationLifetime lifetime) => new(
TimeProvider.System, lifetime, NullLogger<DesktopUserRequestStore>.Instance);
}