using FsCheck; using FsCheck.Xunit; using Xunit; using XiangYi.Application.Services; using XiangYi.Core.Entities.Biz; using XiangYi.Core.Enums; namespace XiangYi.Application.Tests.Services; /// /// ChatService属性测试 - 消息发送持久化 /// public class MessagePersistencePropertyTests { /// /// **Feature: backend-api, Property 14: 消息发送持久化** /// **Validates: Requirements 6.1, 6.2** /// /// *For any* 发送的聊天消息, 应被正确保存到数据库且可通过消息列表查询到 /// [Property(MaxTest = 100)] public Property Message_ShouldBePersisted_AndQueryable() { // 生成消息ID(1-10000) var messageIdArb = Gen.Choose(1, 10000); // 生成消息类型(文本或语音) var messageTypeArb = Gen.Elements((int)MessageType.Text, (int)MessageType.Voice); // 生成非空消息内容 var contentArb = Gen.Elements("Hello", "你好", "Test message", "测试消息", "Hi there"); return Prop.ForAll( messageIdArb.ToArbitrary(), messageTypeArb.ToArbitrary(), contentArb.ToArbitrary(), (messageId, messageType, content) => { var voiceUrl = "https://example.com/voice.mp3"; // Arrange - 创建消息 var message = new ChatMessage { Id = messageId, SessionId = 1, SenderId = 1, ReceiverId = 2, MessageType = messageType, Content = messageType == (int)MessageType.Text ? content : null, VoiceUrl = messageType == (int)MessageType.Voice ? voiceUrl : null, Status = (int)MessageStatus.Normal, IsRead = false, CreateTime = DateTime.Now }; // 模拟消息列表(持久化后的状态) var messages = new List { message }; // Act - 验证消息是否应该被持久化 var shouldPersist = ChatService.ShouldMessageBePersisted( messageType, message.Content, message.VoiceUrl); // 验证消息是否可以在列表中查询到 var isInList = ChatService.IsMessageInList(messages, messageId); // Assert - 如果消息应该被持久化,则应该可以在列表中查询到 return shouldPersist && isInList; }); } /// /// 文本消息 - 有内容时应该被持久化 /// [Property(MaxTest = 100)] public Property TextMessage_WithContent_ShouldBePersisted() { var contentArb = Gen.Elements("Hello", "你好", "Test", "消息内容", "Some text"); return Prop.ForAll( contentArb.ToArbitrary(), content => { var shouldPersist = ChatService.ShouldMessageBePersisted( (int)MessageType.Text, content, null); return shouldPersist; }); } /// /// 文本消息 - 空内容时不应该被持久化 /// [Property(MaxTest = 100)] public Property TextMessage_EmptyContent_ShouldNotBePersisted() { var emptyContentArb = Gen.Elements("", " ", null, "\t", "\n"); return Prop.ForAll( emptyContentArb.ToArbitrary(), emptyContent => { var shouldPersist = ChatService.ShouldMessageBePersisted( (int)MessageType.Text, emptyContent, null); return !shouldPersist; }); } /// /// 语音消息 - 有URL时应该被持久化 /// [Property(MaxTest = 100)] public Property VoiceMessage_WithUrl_ShouldBePersisted() { var voiceUrlArb = Gen.Elements( "https://example.com/voice1.mp3", "https://example.com/voice2.mp3", "https://storage.example.com/audio/123.wav"); return Prop.ForAll( voiceUrlArb.ToArbitrary(), voiceUrl => { var shouldPersist = ChatService.ShouldMessageBePersisted( (int)MessageType.Voice, null, voiceUrl); return shouldPersist; }); } /// /// 语音消息 - 无URL时不应该被持久化 /// [Property(MaxTest = 100)] public Property VoiceMessage_EmptyUrl_ShouldNotBePersisted() { var emptyUrlArb = Gen.Elements("", " ", null, "\t"); return Prop.ForAll( emptyUrlArb.ToArbitrary(), emptyUrl => { var shouldPersist = ChatService.ShouldMessageBePersisted( (int)MessageType.Voice, null, emptyUrl); return !shouldPersist; }); } /// /// 消息查询 - 正常状态的消息应该可以查询到 /// [Property(MaxTest = 100)] public Property Message_NormalStatus_ShouldBeQueryable() { var messageIdArb = Gen.Choose(1, 10000); return Prop.ForAll( messageIdArb.ToArbitrary(), messageId => { var messages = new List { new ChatMessage { Id = messageId, Status = (int)MessageStatus.Normal, CreateTime = DateTime.Now } }; var isInList = ChatService.IsMessageInList(messages, messageId); return isInList; }); } /// /// 消息查询 - 已删除的消息不应该被查询到 /// [Property(MaxTest = 100)] public Property Message_DeletedStatus_ShouldNotBeQueryable() { var messageIdArb = Gen.Choose(1, 10000); return Prop.ForAll( messageIdArb.ToArbitrary(), messageId => { var messages = new List { new ChatMessage { Id = messageId, Status = (int)MessageStatus.Deleted, CreateTime = DateTime.Now } }; var isInList = ChatService.IsMessageInList(messages, messageId); return !isInList; }); } /// /// 消息查询 - 已撤回的消息不应该被查询到 /// [Property(MaxTest = 100)] public Property Message_RecalledStatus_ShouldNotBeQueryable() { var messageIdArb = Gen.Choose(1, 10000); return Prop.ForAll( messageIdArb.ToArbitrary(), messageId => { var messages = new List { new ChatMessage { Id = messageId, Status = (int)MessageStatus.Recalled, CreateTime = DateTime.Now } }; var isInList = ChatService.IsMessageInList(messages, messageId); return !isInList; }); } /// /// 消息查询 - 不存在的消息ID不应该被查询到 /// [Property(MaxTest = 100)] public Property Message_NonExistentId_ShouldNotBeQueryable() { var existingIdArb = Gen.Choose(1, 1000); var nonExistentIdArb = Gen.Choose(1001, 2000); return Prop.ForAll( existingIdArb.ToArbitrary(), nonExistentIdArb.ToArbitrary(), (existingId, nonExistentId) => { var messages = new List { new ChatMessage { Id = existingId, Status = (int)MessageStatus.Normal, CreateTime = DateTime.Now } }; var existingInList = ChatService.IsMessageInList(messages, existingId); var nonExistentInList = ChatService.IsMessageInList(messages, nonExistentId); return existingInList && !nonExistentInList; }); } } /// /// ChatService属性测试 - 交换请求状态流转 /// public class ExchangeStatusTransitionPropertyTests { /// /// **Feature: backend-api, Property 15: 交换请求状态流转** /// **Validates: Requirements 6.3, 6.4, 6.5** /// /// *For any* 交换微信/照片请求, 状态应从"待响应"流转到"已同意"或"已拒绝" /// [Property(MaxTest = 100)] public Property ExchangeRequest_StatusTransition_ShouldBeValid() { // 生成是否同意的布尔值 var isAgreedArb = Gen.Elements(true, false); return Prop.ForAll( isAgreedArb.ToArbitrary(), isAgreed => { // Arrange - 初始状态为待响应(0) var initialStatus = 0; // Act - 计算状态流转 var newStatus = ChatService.CalculateExchangeStatusTransition(initialStatus, isAgreed); // Assert - 验证状态流转是否有效 var isValidTransition = ChatService.IsValidExchangeStatusTransition(initialStatus, newStatus); var isCorrectStatus = ChatService.IsExchangeResponseStatusCorrect(initialStatus, isAgreed, newStatus); return isValidTransition && isCorrectStatus; }); } /// /// 交换请求 - 同意后状态应为1 /// [Property(MaxTest = 100)] public Property ExchangeRequest_Agreed_StatusShouldBeOne() { return Prop.ForAll( Arb.Default.PositiveInt(), _ => { var initialStatus = 0; var newStatus = ChatService.CalculateExchangeStatusTransition(initialStatus, isAgreed: true); return newStatus == 1; }); } /// /// 交换请求 - 拒绝后状态应为2 /// [Property(MaxTest = 100)] public Property ExchangeRequest_Rejected_StatusShouldBeTwo() { return Prop.ForAll( Arb.Default.PositiveInt(), _ => { var initialStatus = 0; var newStatus = ChatService.CalculateExchangeStatusTransition(initialStatus, isAgreed: false); return newStatus == 2; }); } /// /// 交换请求 - 已响应的请求不能再次流转 /// [Property(MaxTest = 100)] public Property ExchangeRequest_AlreadyResponded_ShouldNotTransition() { // 生成已响应的状态(1或2) var respondedStatusArb = Gen.Elements(1, 2); var isAgreedArb = Gen.Elements(true, false); return Prop.ForAll( respondedStatusArb.ToArbitrary(), isAgreedArb.ToArbitrary(), (respondedStatus, isAgreed) => { // Act - 尝试再次流转 var newStatus = ChatService.CalculateExchangeStatusTransition(respondedStatus, isAgreed); // Assert - 应该返回-1表示无法流转 return newStatus == -1; }); } /// /// 交换请求 - 只有待响应状态可以流转 /// [Property(MaxTest = 100)] public Property ExchangeRequest_OnlyPendingCanTransition() { // 生成各种状态 var statusArb = Gen.Choose(-5, 5); var isAgreedArb = Gen.Elements(true, false); return Prop.ForAll( statusArb.ToArbitrary(), isAgreedArb.ToArbitrary(), (status, isAgreed) => { var newStatus = ChatService.CalculateExchangeStatusTransition(status, isAgreed); // 只有状态为0时才能成功流转 if (status == 0) { return newStatus == (isAgreed ? 1 : 2); } else { return newStatus == -1; } }); } /// /// 交换请求 - 状态流转验证函数正确性 /// [Property(MaxTest = 100)] public Property ExchangeRequest_ValidationFunction_ShouldBeCorrect() { // 生成所有可能的状态组合 var fromStatusArb = Gen.Choose(-2, 5); var toStatusArb = Gen.Choose(-2, 5); return Prop.ForAll( fromStatusArb.ToArbitrary(), toStatusArb.ToArbitrary(), (fromStatus, toStatus) => { var isValid = ChatService.IsValidExchangeStatusTransition(fromStatus, toStatus); // 只有从0到1或从0到2是有效的 var expectedValid = fromStatus == 0 && (toStatus == 1 || toStatus == 2); return isValid == expectedValid; }); } /// /// 交换请求 - 响应状态正确性验证 /// [Property(MaxTest = 100)] public Property ExchangeRequest_ResponseStatusCorrectness() { var isAgreedArb = Gen.Elements(true, false); var newStatusArb = Gen.Choose(0, 3); return Prop.ForAll( isAgreedArb.ToArbitrary(), newStatusArb.ToArbitrary(), (isAgreed, newStatus) => { // 原始状态为待响应 var originalStatus = 0; var isCorrect = ChatService.IsExchangeResponseStatusCorrect(originalStatus, isAgreed, newStatus); // 验证正确性 var expectedCorrect = (isAgreed && newStatus == 1) || (!isAgreed && newStatus == 2); return isCorrect == expectedCorrect; }); } /// /// 交换请求 - 非待响应状态的响应应该失败 /// [Property(MaxTest = 100)] public Property ExchangeRequest_NonPendingStatus_ResponseShouldFail() { // 生成非待响应状态 var nonPendingStatusArb = Gen.Elements(1, 2, 3, -1); var isAgreedArb = Gen.Elements(true, false); var newStatusArb = Gen.Choose(0, 3); return Prop.ForAll( nonPendingStatusArb.ToArbitrary(), isAgreedArb.ToArbitrary(), newStatusArb.ToArbitrary(), (originalStatus, isAgreed, newStatus) => { var isCorrect = ChatService.IsExchangeResponseStatusCorrect(originalStatus, isAgreed, newStatus); // 非待响应状态的响应应该总是失败 return !isCorrect; }); } /// /// 交换请求 - 状态流转的一致性 /// [Property(MaxTest = 100)] public Property ExchangeRequest_StatusTransition_Consistency() { var isAgreedArb = Gen.Elements(true, false); return Prop.ForAll( isAgreedArb.ToArbitrary(), isAgreed => { // 从待响应状态开始 var initialStatus = 0; // 计算新状态 var newStatus = ChatService.CalculateExchangeStatusTransition(initialStatus, isAgreed); // 验证流转有效性 var isValidTransition = ChatService.IsValidExchangeStatusTransition(initialStatus, newStatus); // 验证响应正确性 var isCorrectResponse = ChatService.IsExchangeResponseStatusCorrect(initialStatus, isAgreed, newStatus); // 所有验证应该一致 return isValidTransition && isCorrectResponse; }); } }