diff --git a/Govor.API.Tests/IntegrationTests/EF/Repositories/GroupRepositoryTests.cs b/Govor.API.Tests/IntegrationTests/EF/Repositories/GroupRepositoryTests.cs new file mode 100644 index 0000000..3fac8c6 --- /dev/null +++ b/Govor.API.Tests/IntegrationTests/EF/Repositories/GroupRepositoryTests.cs @@ -0,0 +1,308 @@ +using AutoFixture; +using Govor.Core.Infrastructure.Validators; +using Govor.Core.Models; +using Govor.Data; +using Govor.Data.Repositories; +using Govor.Data.Repositories.Exceptions; +using Microsoft.EntityFrameworkCore; + +namespace Govor.API.Tests.IntegrationTests.EF.Repositories; + +[TestFixture] +public class GroupRepositoryTests +{ + private Fixture _fixture; + private DbContextOptions _options; + private IObjectValidator _validator = new ChatGroupValidator(); + private int _testIteration = 0; + + [SetUp] + public void SetUp() + { + _testIteration += 1; + + _fixture = new Fixture(); + + _fixture.Behaviors + .OfType() + .ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + + _options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: $"DbGovor_{nameof(GroupRepositoryTests)}_{_testIteration}") + .Options; + } + + [Test] + public async Task Given_NotEmptySetDb_When_GetAll_Then_ReturnAll() + { + // Arrange + var random = new Random(); + var chats = _fixture.CreateMany(random.Next(2, 10)).ToList(); + + await using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + context.ChatGroups.AddRange(chats); + await context.SaveChangesAsync(); + + // Act + + var result = await repository.GetAllAsync(); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Count, Is.EqualTo(chats.Count)); + Assert.That(result, Is.EquivalentTo(chats)); + } + + [Test] + public void Given_EmptySetDb_When_GetAll_Should_Throw_NotFoundException() + { + // Arrange + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act & Assert + Assert.ThrowsAsync(async () => await repository.GetAllAsync()); + } + + [Test] + public async Task Given_ValidChatId_When_GetById_Then_ReturnChat() + { + // Arrange + var chats = _fixture.CreateMany(10); + var id = chats.First().Id; + + await using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + context.ChatGroups.AddRange(chats); + await context.SaveChangesAsync(); + + // Act + var result = await repository.GetByIdAsync(id); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.EqualTo(chats.First())); + } + + [Test] + public void Given_InvalidMessageId_When_FindById_Should_Throw_NotFoundByKeyException() + { + // Arrange + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act & Assert + Assert.ThrowsAsync>(async () => + await repository.GetByIdAsync(_fixture.Create())); + } + + [Test] + public async Task Given_ValidQuery_When_SearchByNameAsync_Then_Returns_ChatGroups() + { + // Arrange + var random = new Random(); + var chats = _fixture.CreateMany(random.Next(3, 10)).ToList(); + + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + context.ChatGroups.AddRange(chats); + await context.SaveChangesAsync(); + + // Act + var result = await repository.SearchByNameAsync(chats[0].Name[..14]); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Count, Is.EqualTo(1)); + Assert.That(result.First(), Is.EqualTo(chats.First())); + } + + [Test] + public void Given_InvalidQuery_When_SearchPotentialFriendsAsync_Should_Throw_NotFoundByKeyException() + { + // Arrange + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act & Assert + Assert.ThrowsAsync>(async () => await + repository.SearchByNameAsync(_fixture.Create())); + } + + [Test] + public async Task Given_ValidAdminId_When_GetByAdminIdAsync_Returns_ChatGroups() + { + // Arrange + var random = new Random(); + var chats = _fixture.CreateMany(random.Next(3, 10)).ToList(); + var adminId = chats.First().Admins.First().UserId; + + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + context.ChatGroups.AddRange(chats); + await context.SaveChangesAsync(); + // Act + var result = await repository.GetByAdminIdAsync(adminId); + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Count, Is.EqualTo(1)); + Assert.That(result.First(), Is.EqualTo(chats.First())); + } + + [Test] + public void Given_ValidInvalidAdminId_When_GetByAdminIdAsync_Throws_NotFoundByKeyException() + { + // Arrange + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act & Assert + Assert.ThrowsAsync>(async () => await + repository.GetByAdminIdAsync(_fixture.Create())); + } + [Test] + public async Task Given_ValidMemberId_When_GetByAdminIdAsync_Returns_ChatGroups() + { + // Arrange + var random = new Random(); + var chats = _fixture.CreateMany(random.Next(3, 10)).ToList(); + var userId = chats.First().Members.First().UserId; + + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + context.ChatGroups.AddRange(chats); + await context.SaveChangesAsync(); + // Act + var result = await repository.GetByUserIdAsync(userId); + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Count, Is.EqualTo(1)); + Assert.That(result.First(), Is.EqualTo(chats.First())); + } + + [Test] + public void Given_ValidInvalidMemberId_When_GetByAdminIdAsync_Throws_NotFoundByKeyException() + { + // Arrange + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act & Assert + Assert.ThrowsAsync>(async () => await + repository.GetByUserIdAsync(_fixture.Create())); + } + + [Test] + public async Task Given_ValidChatGroup_When_AddAsync_Then_PrivateChatAdded() + { + // Arrange + var chat = _fixture.Create(); + + await using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act + await repository.AddAsync(chat); + + // Assert + Assert.That(context.ChatGroups.Count, Is.EqualTo(1)); + Assert.That(context.ChatGroups.First(), Is.EqualTo(chat)); + } + + [Test] + public void Given_InvalidChatGroup_When_AddAsync_Should_Throw_AdditionException() + { + // Arrange + using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act & Assert + Assert.ThrowsAsync(async () => await repository.AddAsync(default)); + } + + [Test] + public async Task Given_ExistChatGroup_When_Exist_Then_ReturnTrue() + { + // Arrange + var chat = _fixture.Create(); + + await using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + context.ChatGroups.Add(chat); + await context.SaveChangesAsync(); + + // Act + var result1 = repository.Exist(chat.Id); + var result2 = repository.Exist(chat); + + + // Assert + Assert.That(result1, Is.True); + Assert.That(result2, Is.True); + } + + [Test] + public async Task Given_NotExistChatGroup_When_Exist_Then_ReturnFalse() + { + // Arrange + var chat = _fixture.Create(); + + await using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act + var result1 = repository.Exist(chat.Id); + var result2 = repository.Exist(chat); + + // Assert + Assert.That(result1, Is.False); + Assert.That(result2, Is.False); + } + + [Test] + public async Task Given_ValidUserIdAndChatGroupId_When_IsUserMemberOfGroupAsync_Then_ReturnTrue() + { + // Arrange + var chat = _fixture.Create(); + var userId = chat.Members.First().UserId; + + await using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + await context.ChatGroups.AddAsync(chat); + await context.SaveChangesAsync(); + + // Act + var result = await repository.IsUserMemberOfGroupAsync(userId, chat.Id); + + // Assert + Assert.That(result, Is.True); + } + + [Test] + public async Task Given_InvalidValidUserIdAndChatGroupId_When_IsUserMemberOfGroupAsync_Then_ReturnFalse() + { + // Arrange + var chat = _fixture.Create(); + var userId = chat.Members.First().UserId; + + await using var context = new GovorDbContext(_options); + var repository = new GroupRepository(context, _validator); + + // Act + var result = await repository.IsUserMemberOfGroupAsync(userId, chat.Id); + + // Assert + Assert.That(result, Is.False); + } +} \ No newline at end of file diff --git a/Govor.API.Tests/IntegrationTests/EF/Repositories/PrivateChatsRepositoryTests.cs b/Govor.API.Tests/IntegrationTests/EF/Repositories/PrivateChatsRepositoryTests.cs index 90729ca..387beff 100644 --- a/Govor.API.Tests/IntegrationTests/EF/Repositories/PrivateChatsRepositoryTests.cs +++ b/Govor.API.Tests/IntegrationTests/EF/Repositories/PrivateChatsRepositoryTests.cs @@ -43,14 +43,14 @@ public class PrivateChatsRepositoryTests var chats = _fixture.CreateMany(random.Next(2, 10)).ToList(); await using var context = new GovorDbContext(_options); - var messagesRepository = new PrivateChatsRepository(context, _validator); + var repository = new PrivateChatsRepository(context, _validator); context.PrivateChats.AddRange(chats); await context.SaveChangesAsync(); // Act - var result = await messagesRepository.GetAllAsync(); + var result = await repository.GetAllAsync(); // Assert Assert.That(result, Is.Not.Null); @@ -134,7 +134,7 @@ public class PrivateChatsRepositoryTests } [Test] - public async Task Given_ValidMessage_When_AddAsync_Then_MessageAdded() + public async Task Given_ValidPrivateChat_When_AddAsync_Then_PrivateChatAdded() { // Arrange var chat = _fixture.Create(); @@ -151,7 +151,7 @@ public class PrivateChatsRepositoryTests } [Test] - public async Task Given_InvalidMessage_When_AddAsync_Should_Throw_AdditionException() + public async Task Given_InvalidPrivateChat_When_AddAsync_Should_Throw_AdditionException() { // Arrange await using var context = new GovorDbContext(_options); @@ -162,7 +162,7 @@ public class PrivateChatsRepositoryTests } [Test] - public async Task Given_ExistMessage_When_Exist_Then_ReturnTrue() + public async Task Given_ExistPrivateChat_When_Exist_Then_ReturnTrue() { // Arrange var chat = _fixture.Create(); @@ -184,7 +184,7 @@ public class PrivateChatsRepositoryTests } [Test] - public async Task Given_NotExistMessage_When_Exist_Then_ReturnFalse() + public async Task Given_NotExistPrivateChat_When_Exist_Then_ReturnFalse() { // Arrange var chat = _fixture.Create(); @@ -200,4 +200,5 @@ public class PrivateChatsRepositoryTests Assert.That(result1, Is.False); Assert.That(result2, Is.False); } + } \ No newline at end of file diff --git a/Govor.API.Tests/UnitTests/Services/MessageServiceTests.cs b/Govor.API.Tests/UnitTests/Services/MessageServiceTests.cs index 8aefb5b..d415335 100644 --- a/Govor.API.Tests/UnitTests/Services/MessageServiceTests.cs +++ b/Govor.API.Tests/UnitTests/Services/MessageServiceTests.cs @@ -94,7 +94,7 @@ public class MessageServiceTests DateTime.UtcNow, new List()); - _mockGroupsRepo.Setup(r => r.Exists(groupId)).Returns(true); + _mockGroupsRepo.Setup(r => r.Exist(groupId)).Returns(true); _mockGroupsRepo.Setup(r => r.IsUserMemberOfGroupAsync(senderId, groupId)).ReturnsAsync(true); _mockMessagesRepo.Setup(r => r.AddAsync(It.IsAny())).Returns(Task.CompletedTask); diff --git a/Govor.Application/Services/MessageService.cs b/Govor.Application/Services/MessageService.cs index f894666..e39af2f 100644 --- a/Govor.Application/Services/MessageService.cs +++ b/Govor.Application/Services/MessageService.cs @@ -54,7 +54,7 @@ public class MessageService : IMessageService } else if (sendParams.RecipientType == RecipientType.Group) { - if (!_groupsRepository.Exists(sendParams.RecipientId)) + if (!_groupsRepository.Exist(sendParams.RecipientId)) { _logger.LogWarning("Attempt to send message to non-existent group {GroupId}", sendParams.RecipientId); return new SendMessageResult(false, new KeyNotFoundException($"Recipient group {sendParams.RecipientId} not found."), default); diff --git a/Govor.Core/Models/ChatGroup.cs b/Govor.Core/Models/ChatGroup.cs index 7954c97..beea1ee 100644 --- a/Govor.Core/Models/ChatGroup.cs +++ b/Govor.Core/Models/ChatGroup.cs @@ -11,4 +11,16 @@ public class ChatGroup public List Admins { get; set; } = new(); public List Members { get; set; } = new(); public List InviteCodes { get; set; } = new(); + + public override bool Equals(object? obj) + { + ChatGroup chatGroup = obj as ChatGroup; + + return Id == chatGroup.Id && + Name == chatGroup.Name && + Description == chatGroup.Description && + ImageId == chatGroup.ImageId && + IsChannel == chatGroup.IsChannel && + IsPrivate == chatGroup.IsPrivate; + } } \ No newline at end of file diff --git a/Govor.Core/Repositories/Groups/IGroupsExist.cs b/Govor.Core/Repositories/Groups/IGroupsExist.cs index 4e95afa..f6b763f 100644 --- a/Govor.Core/Repositories/Groups/IGroupsExist.cs +++ b/Govor.Core/Repositories/Groups/IGroupsExist.cs @@ -4,6 +4,6 @@ namespace Govor.Core.Repositories.Groups; public interface IGroupsExist { - public bool Exists(Guid groupId); - public bool Exists(ChatGroup chatGroup); + public bool Exist(Guid groupId); + public bool Exist(ChatGroup chatGroup); } \ No newline at end of file diff --git a/Govor.Data/Configurations/ChatGroupConfigurator.cs b/Govor.Data/Configurations/ChatGroupConfigurator.cs new file mode 100644 index 0000000..f2f5c90 --- /dev/null +++ b/Govor.Data/Configurations/ChatGroupConfigurator.cs @@ -0,0 +1,31 @@ +using Govor.Core.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Govor.Data.Configurations; + +public class ChatGroupConfigurator : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(e => e.Id); + + builder.Property(e => e.Name).IsRequired().HasMaxLength(100); + builder.Property(e => e.Description).HasMaxLength(500); + + builder.HasMany(e => e.Members) + .WithOne() + .HasForeignKey(e => e.GroupId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(e => e.Admins) + .WithOne() + .HasForeignKey(e => e.GroupId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(e => e.InviteCodes) + .WithOne() + .HasForeignKey(e => e.GroupId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/Govor.Data/Configurations/GroupAdminsConfiguration.cs b/Govor.Data/Configurations/GroupAdminsConfiguration.cs new file mode 100644 index 0000000..92866ff --- /dev/null +++ b/Govor.Data/Configurations/GroupAdminsConfiguration.cs @@ -0,0 +1,16 @@ +using Govor.Core.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Govor.Data.Configurations; + +public class GroupAdminsConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(e => e.Id); + + builder.Property(e => e.UserId).IsRequired(); + builder.Property(e => e.GroupId).IsRequired(); + } +} \ No newline at end of file diff --git a/Govor.Data/Configurations/GroupInvitationConfiguration.cs b/Govor.Data/Configurations/GroupInvitationConfiguration.cs new file mode 100644 index 0000000..4d92246 --- /dev/null +++ b/Govor.Data/Configurations/GroupInvitationConfiguration.cs @@ -0,0 +1,26 @@ +using Govor.Core.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Govor.Data.Configurations; + +public class GroupInvitationConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(e => e.Id); + + builder.Property(e => e.InvitationCode).IsRequired().HasMaxLength(200); + builder.Property(e => e.Description).HasMaxLength(500); + builder.Property(e => e.EndDate).IsRequired(); + builder.Property(e => e.CreatedAt).IsRequired(); + + builder.HasOne(e => e.UserMaker) + .WithMany() + .HasForeignKey(e => e.UserMakerId) + .OnDelete(DeleteBehavior.Restrict); + + + builder.Ignore(e => e.GroupMemberships); + } +} \ No newline at end of file diff --git a/Govor.Data/Configurations/GroupMembershipConfiguration.cs b/Govor.Data/Configurations/GroupMembershipConfiguration.cs index 812aa8b..9cee30b 100644 --- a/Govor.Data/Configurations/GroupMembershipConfiguration.cs +++ b/Govor.Data/Configurations/GroupMembershipConfiguration.cs @@ -8,6 +8,16 @@ public class GroupMembershipConfiguration : IEntityTypeConfiguration builder) { - builder.HasKey(m => new { m.GroupId, m.UserId }); + builder.HasKey(e => e.Id); + + builder.Property(e => e.UserId).IsRequired(); + builder.Property(e => e.GroupId).IsRequired(); + builder.Property(e => e.InvitationId).IsRequired(); + + // Optional: можно добавить навигацию к GroupInvitation + builder.HasOne() + .WithMany() + .HasForeignKey(e => e.InvitationId) + .OnDelete(DeleteBehavior.SetNull); } } \ No newline at end of file diff --git a/Govor.Data/GovorDbContext.cs b/Govor.Data/GovorDbContext.cs index b49c3e8..5414310 100644 --- a/Govor.Data/GovorDbContext.cs +++ b/Govor.Data/GovorDbContext.cs @@ -36,6 +36,10 @@ public class GovorDbContext(DbContextOptions options) : DbContex modelBuilder.ApplyConfiguration(new MediaAttachmentsConfiguration()); modelBuilder.ApplyConfiguration(new MessageViewConfiguration()); modelBuilder.ApplyConfiguration(new MediaFileConfiguration()); + modelBuilder.ApplyConfiguration(new ChatGroupConfigurator()); + modelBuilder.ApplyConfiguration(new GroupInvitationConfiguration()); + modelBuilder.ApplyConfiguration(new GroupMembershipConfiguration()); + modelBuilder.ApplyConfiguration(new GroupAdminsConfiguration()); base.OnModelCreating(modelBuilder); } diff --git a/Govor.Data/Repositories/GroupRepository.cs b/Govor.Data/Repositories/GroupRepository.cs index daaf32a..f765551 100644 --- a/Govor.Data/Repositories/GroupRepository.cs +++ b/Govor.Data/Repositories/GroupRepository.cs @@ -139,12 +139,12 @@ public class GroupRepository : IGroupsRepository } } - public bool Exists(Guid groupId) + public bool Exist(Guid groupId) { return _context.ChatGroups.Any(g => g.Id == groupId); } - public bool Exists(ChatGroup chatGroup) + public bool Exist(ChatGroup chatGroup) { return _context.ChatGroups.Any(g => g.Id == chatGroup.Id && g.IsChannel == chatGroup.IsChannel &&