92 lines
3.2 KiB
C#
92 lines
3.2 KiB
C#
using Microsoft.Extensions.Options;
|
|
using MyAgent.Core;
|
|
using Xunit;
|
|
|
|
namespace MyAgent.Tests.Core;
|
|
|
|
public sealed class SessionManagerTests
|
|
{
|
|
[Fact]
|
|
public async Task DebounceAsync_TwoRapidMessagesProduceOneStableBatch()
|
|
{
|
|
var manager = CreateManager();
|
|
var now = DateTimeOffset.Now;
|
|
var first = manager.DebounceAsync(CreateMessage("1", "第一条", now));
|
|
await Task.Delay(10);
|
|
var second = manager.DebounceAsync(CreateMessage("2", "第二条", now.AddMilliseconds(10)));
|
|
|
|
Assert.Null(await first);
|
|
var batch = await second;
|
|
Assert.NotNull(batch);
|
|
Assert.Equal(2, batch!.Messages.Count);
|
|
Assert.Contains("第一条", batch.MergedText);
|
|
Assert.Contains("第二条", batch.MergedText);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DebounceAsync_CompletedBatchIsConsumed()
|
|
{
|
|
var manager = CreateManager();
|
|
var first = await manager.DebounceAsync(CreateMessage("1", "第一轮", DateTimeOffset.Now));
|
|
var second = await manager.DebounceAsync(CreateMessage("2", "第二轮", DateTimeOffset.Now.AddSeconds(1)));
|
|
|
|
Assert.Single(first!.Messages);
|
|
Assert.Single(second!.Messages);
|
|
Assert.Equal("第二轮", second.MergedText);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DebounceAsync_CallerCancellationDoesNotLeaveBrokenPendingState()
|
|
{
|
|
var manager = CreateManager();
|
|
using var cancellation = new CancellationTokenSource();
|
|
var cancelled = manager.DebounceAsync(CreateMessage("1", "取消", DateTimeOffset.Now), cancellation.Token);
|
|
cancellation.Cancel();
|
|
|
|
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => cancelled);
|
|
var next = await manager.DebounceAsync(CreateMessage("2", "下一条", DateTimeOffset.Now.AddSeconds(1)));
|
|
|
|
Assert.NotNull(next);
|
|
Assert.Single(next!.Messages);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DebounceAsync_DifferentUsersReplyingToSameMessageStayIsolated()
|
|
{
|
|
var manager = CreateManager();
|
|
var firstMessage = CreateMessage("1", "用户一", DateTimeOffset.Now, "1", "root");
|
|
var secondMessage = CreateMessage("2", "用户二", DateTimeOffset.Now, "2", "root");
|
|
|
|
var first = manager.DebounceAsync(firstMessage);
|
|
var second = manager.DebounceAsync(secondMessage);
|
|
var batches = await Task.WhenAll(first, second);
|
|
|
|
Assert.All(batches, batch => Assert.Single(batch!.Messages));
|
|
Assert.NotEqual(batches[0]!.ThreadId, batches[1]!.ThreadId);
|
|
}
|
|
|
|
private static SessionManager CreateManager()
|
|
{
|
|
var options = Options.Create(new ShirobotOptions
|
|
{
|
|
Fusion = new FusionOptions
|
|
{
|
|
DebounceMs = 40,
|
|
SameUserMergeWindowSec = 8,
|
|
MaxMergedMessages = 8
|
|
}
|
|
});
|
|
return new SessionManager(options, TimeProvider.System);
|
|
}
|
|
|
|
private static IncomingMessage CreateMessage(string id, string text, DateTimeOffset at, string userId = "1", string? replyToMessageId = null) => new()
|
|
{
|
|
MessageId = id,
|
|
GroupId = "1",
|
|
UserId = userId,
|
|
Text = text,
|
|
CreatedAt = at,
|
|
ReplyToMessageId = replyToMessageId
|
|
};
|
|
}
|