diff --git a/Govor.API.Tests/UnitTests/Services/MessageServiceTests.cs b/Govor.API.Tests/UnitTests/Services/MessageServiceTests.cs new file mode 100644 index 0000000..d789064 --- /dev/null +++ b/Govor.API.Tests/UnitTests/Services/MessageServiceTests.cs @@ -0,0 +1,318 @@ +using Govor.Application.Exceptions.VerifyFriendship; +using Govor.Application.Interfaces; +using Govor.Application.Interfaces.Messages.Parameters; +using Govor.Application.Services; +using Govor.Core.Models; +using Govor.Core.Repositories.Groups; +using Govor.Core.Repositories.Messages; +using Govor.Core.Repositories.Users; +using Govor.Data.Repositories.Exceptions; +using Microsoft.Extensions.Logging; +using Moq; + +namespace Govor.API.Tests.UnitTests.Services; + +[TestFixture] +public class MessageServiceTests +{ + private Mock _mockMessagesRepo; + private Mock _mockUsersRepo; + private Mock _mockGroupsRepo; + private Mock _mockVerifyFriendship; + private Mock> _mockLogger; + private MessageService _messageService; + + [SetUp] + public void SetUp() + { + _mockMessagesRepo = new Mock(); + _mockUsersRepo = new Mock(); + _mockGroupsRepo = new Mock(); + _mockVerifyFriendship = new Mock(); + _mockLogger = new Mock>(); + + _messageService = new MessageService( + _mockMessagesRepo.Object, + _mockUsersRepo.Object, + _mockGroupsRepo.Object, + _mockVerifyFriendship.Object, + _mockLogger.Object); + } + + + // Test for SendMessageAsync action + [Test] + public async Task SendMessageAsync_ToUser_Success() + { + // Arrange + var senderId = Guid.NewGuid(); + var recipientId = Guid.NewGuid(); + + var sendMessageParams = new SendMessage("Hello", + null, + recipientId, + RecipientType.User, + senderId, + DateTime.UtcNow, + new List()); + + _mockUsersRepo.Setup(r => r.ExistsByIdAsync(recipientId)).ReturnsAsync(true); + _mockVerifyFriendship.Setup(v => v.VerifyAsync(senderId, recipientId)).Returns(Task.CompletedTask); + _mockMessagesRepo.Setup(r => r.AddAsync(It.IsAny())).Returns(Task.CompletedTask); + + // Act + var result = await _messageService.SendMessageAsync(sendMessageParams); + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(true)); + Assert.That(result.Exception, Is.Null); + + _mockMessagesRepo.Verify(r => r.AddAsync(It.Is(m => + m.SenderId == senderId && + m.RecipientId == recipientId && + m.RecipientType == RecipientType.User && + m.EncryptedContent == "Hello")), Times.Once); + } + + [Test] + public async Task SendMessageAsync_ToGroup_Success() + { + // Arrange + var senderId = Guid.NewGuid(); + var groupId = Guid.NewGuid(); + + var sendMessageParams = new SendMessage("Hello Group", + null, + groupId, + RecipientType.Group, + senderId, + DateTime.UtcNow, + new List()); + + _mockGroupsRepo.Setup(r => r.Exists(groupId)).Returns(true); + _mockGroupsRepo.Setup(r => r.IsUserMemberOfGroupAsync(senderId, groupId)).Returns(true); + _mockMessagesRepo.Setup(r => r.AddAsync(It.IsAny())).Returns(Task.CompletedTask); + + // Act + var result = await _messageService.SendMessageAsync(sendMessageParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(true)); + Assert.That(result.Exception, Is.Null); + _mockMessagesRepo.Verify(r => r.AddAsync(It.Is(m => + m.RecipientId == groupId && + m.RecipientType == RecipientType.Group)), Times.Once); + } + + [Test] + public async Task SendMessageAsync_ToUser_RecipientNotFound_ReturnsFailure() + { + // Arrange + var senderId = Guid.NewGuid(); + var recipientId = Guid.NewGuid(); + + var sendMessageParams = new SendMessage("Hello", + null, + recipientId, + RecipientType.User, + senderId, + DateTime.UtcNow, + new List()); + + _mockUsersRepo.Setup(r => r.ExistsByIdAsync(recipientId)).ReturnsAsync(false); + + // Act + var result = await _messageService.SendMessageAsync(sendMessageParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(false)); + Assert.That(result.Exception, Is.Not.Null); + Assert.That(result.Exception,Is.TypeOf()); + Assert.That(result.MessageId, Is.EqualTo(Guid.Empty)); + } + + [Test] + public async Task SendMessageAsync_ToUser_FriendshipVerificationFails_ReturnsFailure() + { + // Arrange + var senderId = Guid.NewGuid(); + var recipientId = Guid.NewGuid(); + + var sendMessageParams = new SendMessage("Hello", + null, + recipientId, + RecipientType.User, + senderId, + DateTime.UtcNow, + new List() + ); + + _mockUsersRepo.Setup(r => r.ExistsByIdAsync(recipientId)).ReturnsAsync(true); + _mockVerifyFriendship.Setup(v => v.VerifyAsync(senderId, recipientId)).ThrowsAsync(new FriendshipException("Not friends")); + + // Act + var result = await _messageService.SendMessageAsync(sendMessageParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(false)); + Assert.That(result.Exception, Is.Not.Null); + Assert.That(result.Exception,Is.TypeOf()); + Assert.That(result.MessageId, Is.EqualTo(Guid.Empty)); + } + + // Test for EditMessageAsync action + [Test] + public async Task EditMessageAsync_Success() + { + // Arrange + var editorId = Guid.NewGuid(); + var messageId = Guid.NewGuid(); + + var originalMessage = new Message + { + Id = messageId, + SenderId = editorId, + EncryptedContent = "Old", + RecipientId = Guid.NewGuid(), + RecipientType = RecipientType.User + }; + + var editParams = new EditMessage(editorId, messageId, "New Content", DateTime.UtcNow); + + _mockMessagesRepo.Setup(r => r.FindByIdAsync(messageId)).ReturnsAsync(originalMessage); + _mockMessagesRepo.Setup(r => r.UpdateAsync(It.IsAny())).Returns(Task.CompletedTask); + + // Act + var result = await _messageService.EditMessageAsync(editParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(true)); + Assert.That(result.OriginalMessage, Is.Not.Null); + + Assert.That(messageId, Is.EqualTo(result.OriginalMessage!.Id)); + + _mockMessagesRepo.Verify(r => r.UpdateAsync(It.Is(m => + m.Id == messageId && + m.EncryptedContent == "New Content" && + m.IsEdited == true && + m.EditedAt == editParams.EditedAt)), Times.Once); + } + + [Test] + public async Task EditMessageAsync_MessageNotFound_ReturnsFailure() + { + // Arrange + var editorId = Guid.NewGuid(); + var messageId = Guid.NewGuid(); + var editParams = new EditMessage(editorId, messageId, "New Content", DateTime.UtcNow); + + _mockMessagesRepo.Setup(r => r.FindByIdAsync(messageId)). + ThrowsAsync(new NotFoundByKeyException(messageId)); + + // Act + var result = await _messageService.EditMessageAsync(editParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(false)); + Assert.That(result.Exception, Is.Not.Null); + Assert.That(result.Exception,Is.TypeOf>()); + Assert.That(result.OriginalMessage, Is.Null); + } + + [Test] + public async Task EditMessageAsync_NotSender_ReturnsFailure() + { + // Arrange + var editorId = Guid.NewGuid(); + var senderId = Guid.NewGuid(); // Different from editorId + var messageId = Guid.NewGuid(); + var originalMessage = new Message { Id = messageId, SenderId = senderId, EncryptedContent = "Old" }; + var editParams = new EditMessage(editorId, messageId, "New Content", DateTime.UtcNow); + + _mockMessagesRepo.Setup(r => r.FindByIdAsync(messageId)).ReturnsAsync(originalMessage); + + // Act + var result = await _messageService.EditMessageAsync(editParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(false)); + Assert.That(result.Exception, Is.Not.Null); + Assert.That(result.Exception,Is.TypeOf()); + Assert.That(result.OriginalMessage, Is.Null); + } + + // Test for DeleteMessageAsync action + [Test] + public async Task DeleteMessageAsync_Success() + { + // Arrange + var deleterId = Guid.NewGuid(); + var messageId = Guid.NewGuid(); + var originalMessage = new Message { Id = messageId, SenderId = deleterId, RecipientId = Guid.NewGuid(), RecipientType = RecipientType.User }; + var deleteParams = new DeleteMessage(deleterId, messageId); + + _mockMessagesRepo.Setup(r => r.FindByIdAsync(messageId)).ReturnsAsync(originalMessage); + _mockMessagesRepo.Setup(r => r.RemoveAsync(messageId)).Returns(Task.CompletedTask); + + // Act + var result = await _messageService.DeleteMessageAsync(deleteParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(true)); + Assert.That(result.OriginalMessage, Is.Not.Null); + Assert.That(messageId, Is.EqualTo(result.OriginalMessage!.Id)); + _mockMessagesRepo.Verify(r => r.RemoveAsync(messageId), Times.Once); + } + + [Test] + public async Task DeleteMessageAsync_MessageNotFound_ReturnsFailure() + { + // Arrange + var deleterId = Guid.NewGuid(); + var messageId = Guid.NewGuid(); + var deleteParams = new DeleteMessage(deleterId, messageId); + + _mockMessagesRepo.Setup(r => r.FindByIdAsync(messageId)). + ThrowsAsync(new NotFoundByKeyException(messageId)); + + // Act + var result = await _messageService.DeleteMessageAsync(deleteParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(false)); + Assert.That(result.Exception, Is.Not.Null); + Assert.That(result.Exception,Is.TypeOf>()); + Assert.That(result.OriginalMessage, Is.Null); + } + + [Test] + public async Task DeleteMessageAsync_NotSender_ReturnsFailure() + { + // Arrange + var deleterId = Guid.NewGuid(); + var senderId = Guid.NewGuid(); // Different + var messageId = Guid.NewGuid(); + var originalMessage = new Message { Id = messageId, SenderId = senderId }; + var deleteParams = new DeleteMessage(deleterId, messageId); + + _mockMessagesRepo.Setup(r => r.FindByIdAsync(messageId)).ReturnsAsync(originalMessage); + + // Act + var result = await _messageService.DeleteMessageAsync(deleteParams); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(false)); + Assert.That(result.Exception, Is.Not.Null); + Assert.That(result.Exception,Is.TypeOf()); + Assert.That(result.OriginalMessage, Is.Null); + } +} \ No newline at end of file diff --git a/Govor.API/Extensions/ConfigurationProgramExtensions.cs b/Govor.API/Extensions/ConfigurationProgramExtensions.cs index 985c5b7..44d19da 100644 --- a/Govor.API/Extensions/ConfigurationProgramExtensions.cs +++ b/Govor.API/Extensions/ConfigurationProgramExtensions.cs @@ -13,6 +13,7 @@ using Govor.Core.Infrastructure.Validators; using Govor.Core.Models; using Govor.Core.Repositories.Admins; using Govor.Core.Repositories.Friendships; +using Govor.Core.Repositories.Groups; using Govor.Core.Repositories.Invaites; using Govor.Core.Repositories.MediasAttachments; using Govor.Core.Repositories.Messages; @@ -60,6 +61,7 @@ public static class ConfigurationProgramExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } public static void AddValidators(this IServiceCollection services) diff --git a/Govor.API/Hubs/ChatsHub.cs b/Govor.API/Hubs/ChatsHub.cs index dd7f4eb..737db0f 100644 --- a/Govor.API/Hubs/ChatsHub.cs +++ b/Govor.API/Hubs/ChatsHub.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.SignalR; namespace Govor.API.Hubs; -[Authorize] +[Authorize] // api/chats public class ChatsHub : Hub { private readonly ILogger _logger; diff --git a/Govor.Application/Interfaces/Messages/Parameters/SendMessage.cs b/Govor.Application/Interfaces/Messages/Parameters/SendMessage.cs index b302276..f59e222 100644 --- a/Govor.Application/Interfaces/Messages/Parameters/SendMessage.cs +++ b/Govor.Application/Interfaces/Messages/Parameters/SendMessage.cs @@ -5,8 +5,8 @@ namespace Govor.Application.Interfaces.Messages.Parameters; public record SendMessage( string EncryptContent, Guid? ReplyToMessageId, - RecipientType RecipientType, Guid RecipientId, + RecipientType RecipientType, Guid FromUserId, DateTime SendAt, IEnumerable Media); \ No newline at end of file diff --git a/Govor.Core/Models/ChatGroup.cs b/Govor.Core/Models/ChatGroup.cs index 1912474..b1ae3c2 100644 --- a/Govor.Core/Models/ChatGroup.cs +++ b/Govor.Core/Models/ChatGroup.cs @@ -4,6 +4,9 @@ public class ChatGroup { public Guid Id { get; set; } public string Name { get; set; } + public string Description { get; set; } + public Guid ImageId { get; set; } + public List InviteCode { get; set; } public bool IsChannel { get; set; } public bool IsPrivate { get; set; } diff --git a/Govor.Core/Repositories/Groups/IGroupsReader.cs b/Govor.Core/Repositories/Groups/IGroupsReader.cs index df38bb8..752817d 100644 --- a/Govor.Core/Repositories/Groups/IGroupsReader.cs +++ b/Govor.Core/Repositories/Groups/IGroupsReader.cs @@ -1,6 +1,13 @@ +using System.Text.RegularExpressions; + namespace Govor.Core.Repositories.Groups; public interface IGroupsReader { - + public Task> GetAllAsync(); + public Task GetByIdAsync(Guid id); + public Task> FindByNameAsync(string name); + public Task> GetByAdminIdAsync(Guid adminId); + public Task> GetByUserIdAsync(Guid adminId); + public bool IsUserMemberOfGroupAsync(Guid userId, Guid groupId); } \ No newline at end of file diff --git a/Govor.Core/Repositories/Groups/IGroupsRepository.cs b/Govor.Core/Repositories/Groups/IGroupsRepository.cs index cf9482f..9b2faa7 100644 --- a/Govor.Core/Repositories/Groups/IGroupsRepository.cs +++ b/Govor.Core/Repositories/Groups/IGroupsRepository.cs @@ -1,6 +1,6 @@ namespace Govor.Core.Repositories.Groups; -public interface IGroupsRepository : IGroupsReader, IGroupsExist, IGroupsWriter +public interface IGroupsRepository : IGroupsReader, IGroupsWriter, IGroupsExist { } \ No newline at end of file diff --git a/Govor.Core/Repositories/Groups/IGroupsWriter.cs b/Govor.Core/Repositories/Groups/IGroupsWriter.cs index 2fc631c..dbb2ce4 100644 --- a/Govor.Core/Repositories/Groups/IGroupsWriter.cs +++ b/Govor.Core/Repositories/Groups/IGroupsWriter.cs @@ -1,6 +1,10 @@ +using System.Text.RegularExpressions; + namespace Govor.Core.Repositories.Groups; public interface IGroupsWriter { - + Task Add(Group group); + Task Update(Group group); + Task Remove(Guid groupId); } \ No newline at end of file diff --git a/Govor.Data/Repositories/GroupRepository.cs b/Govor.Data/Repositories/GroupRepository.cs index 18b2dbb..cfbc592 100644 --- a/Govor.Data/Repositories/GroupRepository.cs +++ b/Govor.Data/Repositories/GroupRepository.cs @@ -1,6 +1,62 @@ +using System.Text.RegularExpressions; +using Govor.Core.Models; +using Govor.Core.Repositories.Groups; + namespace Govor.Data.Repositories; -public class GroupRepository +public class GroupRepository : IGroupsRepository { + public Task> GetAllAsync() + { + throw new NotImplementedException(); + } + + public Task GetByIdAsync(Guid id) + { + throw new NotImplementedException(); + } + + public Task> FindByNameAsync(string name) + { + throw new NotImplementedException(); + } -} \ No newline at end of file + public Task> GetByAdminIdAsync(Guid adminId) + { + throw new NotImplementedException(); + } + + public Task> GetByUserIdAsync(Guid adminId) + { + throw new NotImplementedException(); + } + + public Task Add(Group group) + { + throw new NotImplementedException(); + } + + public Task Update(Group group) + { + throw new NotImplementedException(); + } + + public Task Remove(Guid groupId) + { + throw new NotImplementedException(); + } + public bool Exists(Guid groupId) + { + throw new NotImplementedException(); + } + + public bool Exists(ChatGroup chatGroup) + { + throw new NotImplementedException(); + } + + public bool IsUserMemberOfGroupAsync(Guid userId, Guid groupId) + { + throw new NotImplementedException(); + } +}