using IM.Application.Chat.Handlers; using Xunit; namespace BuildFlow.IM.Tests.Chat; /// /// 消息撤回权限矩阵与时间窗测试。 /// 测 纯判定逻辑(剥离出 Handler,避免依赖 MongoDB)。 /// public class RecallMessageCommandHandlerTests { private static readonly DateTime Now = new(2026, 6, 27, 10, 0, 0, DateTimeKind.Utc); [Fact] public void Sender_can_recall_within_time_window() { var createdAt = Now.AddSeconds(-30); Assert.True(RecallMessageCommandHandler.CanRecall(isSelf: true, isAdmin: false, createdAt, Now)); } [Fact] public void Sender_cannot_recall_after_time_window() { var createdAt = Now.AddMinutes(-3); // 超过 2 分钟窗口 Assert.False(RecallMessageCommandHandler.CanRecall(isSelf: true, isAdmin: false, createdAt, Now)); } [Fact] public void Sender_can_recall_at_exact_window_boundary() { // 恰好 2 分钟:<= 窗口,允许撤回(边界含等号) var createdAt = Now.AddMinutes(-RecallMessageCommandHandler.RecallTimeWindowMinutes); Assert.True(RecallMessageCommandHandler.CanRecall(isSelf: true, isAdmin: false, createdAt, Now)); } [Fact] public void Non_participant_cannot_recall() { var createdAt = Now.AddSeconds(-10); Assert.False(RecallMessageCommandHandler.CanRecall(isSelf: false, isAdmin: false, createdAt, Now)); } [Fact] public void Group_admin_can_recall_others_message_without_time_limit() { // 管理员撤回他人 1 小时前的消息,不受时间窗限制 var createdAt = Now.AddHours(-1); Assert.True(RecallMessageCommandHandler.CanRecall(isSelf: false, isAdmin: true, createdAt, Now)); } [Fact] public void Group_admin_can_recall_own_message_without_time_limit() { var createdAt = Now.AddHours(-2); Assert.True(RecallMessageCommandHandler.CanRecall(isSelf: true, isAdmin: true, createdAt, Now)); } }