From 70e46e7648d8c9f2d0331f6b6537cd3513a1886e Mon Sep 17 00:00:00 2001 From: Artemy <109195690+stalcker2288969@users.noreply.github.com> Date: Tue, 17 Jun 2025 17:37:10 +0700 Subject: [PATCH] more tests for user rep --- .../EF/Repositories/UsersRepositoryTests.cs | 110 ++++++++++++++++++ .../Validators/IObjectValidator.cs | 2 +- .../Validators/UserValidator.cs | 8 +- Govor.Core/Models/User.cs | 4 +- Govor.Core/Repositories/IUsersReader.cs | 4 +- Govor.Data/Repositories/UsersRepository.cs | 95 ++++++++++++--- 6 files changed, 204 insertions(+), 19 deletions(-) diff --git a/Govor.API.Tests/IntegrationTests/EF/Repositories/UsersRepositoryTests.cs b/Govor.API.Tests/IntegrationTests/EF/Repositories/UsersRepositoryTests.cs index c70dcff..0415ae2 100644 --- a/Govor.API.Tests/IntegrationTests/EF/Repositories/UsersRepositoryTests.cs +++ b/Govor.API.Tests/IntegrationTests/EF/Repositories/UsersRepositoryTests.cs @@ -125,4 +125,114 @@ public class UsersRepositoryTests // Act & Assert Assert.ThrowsAsync>>(async () => await userRepository.FindByRangeId(ids)); } + + [Test] + public async Task Given_ValidUsername_When_FindByUsername_Then_Returns_User() + { + // Arrange + var user = _fixture.Create(); + + await using var context = new GovorDbContext(_options); + var userRepository = new UsersRepository(context, _userValidator); + + context.Users.Add(user); + await context.SaveChangesAsync(); + + // Act + var result = await userRepository.FindByUsername(user.Username); + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Username, Is.EqualTo(user.Username)); + Assert.That(result.Id, Is.EqualTo(user.Id)); + } + + [Test] + public async Task Given_InvalidUsername_When_FindByUsername_Should_Throw_NotFoundByKeyException() + { + // Arrange + string username = _fixture.Create(); + + await using var context = new GovorDbContext(_options); + var userRepository = new UsersRepository(context, _userValidator); + // Act & Assert + + Assert.ThrowsAsync>(async () => await userRepository.FindByUsername(username)); + } + + [Test] + public async Task Given_ValidUsernames_When_FindByRangeUsernames_Then_Returns_Users() + { + // Arrange + var random = new Random(); + var users = _fixture.CreateMany(random.Next(3, 10)).ToList(); + + await using var context = new GovorDbContext(_options); + var userRepository = new UsersRepository(context, _userValidator); + + context.Users.AddRange(users); + await context.SaveChangesAsync(); + + // Act + var result = await userRepository.FindByRangeUsernames(users.Select(u => u.Username)); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result.Select(u => u.Username), Is.EquivalentTo(users.Select(u => u.Username))); + Assert.That(result.Select(u => u.Id), Is.EquivalentTo(users.Select(u => u.Id))); + } + + [Test] + public async Task Given_InvalidUsernames_When_FindByRangeUsernames_Should_Throw_NotFoundByKeyException() + { + // Arrange + var random = new Random(); + var usernames = _fixture.CreateMany(random.Next(3, 10)).ToList(); + + await using var context = new GovorDbContext(_options); + var userRepository = new UsersRepository(context, _userValidator); + + // Act & Assert + Assert.ThrowsAsync>>(async () => await userRepository.FindByRangeUsernames(usernames)); + } + + [Test] + public async Task Given_ValidDateOnly_When_FindByCreatedDate_Then_Returns_Users() + { + // Arrange + var random = new Random(); + var users = _fixture.CreateMany(random.Next(3, 10)).ToList(); + + var selectedDate = users[random.Next(users.Count)].CreatedOn; + + await using var context = new GovorDbContext(_options); + var userRepository = new UsersRepository(context, _userValidator); + + context.Users.AddRange(users); + await context.SaveChangesAsync(); + + // Act + var result = await userRepository.FindUsersByCreatedDate(selectedDate); + + // Assert + var expectedUsers = users + .Where(u => u.CreatedOn == selectedDate) + .ToList(); + + Assert.That(result, Is.Not.Null); + Assert.That(result.Count, Is.EqualTo(expectedUsers.Count)); + Assert.That(result.Select(u => u.Id), Is.EquivalentTo(expectedUsers.Select(u => u.Id))); + } + + [Test] + public async Task Given_InvalidDateOnly_When_FindByCreatedDate_Should_Throw_NotFoundByKeyException() + { + // Arrange + var date = _fixture.Create(); + + await using var context = new GovorDbContext(_options); + var userRepository = new UsersRepository(context, _userValidator); + + // Act & Assert + Assert.ThrowsAsync>(async () => await userRepository.FindUsersByCreatedDate(date)); + } } \ No newline at end of file diff --git a/Govor.Core/Infrastructure/Validators/IObjectValidator.cs b/Govor.Core/Infrastructure/Validators/IObjectValidator.cs index ca05fef..f33f949 100644 --- a/Govor.Core/Infrastructure/Validators/IObjectValidator.cs +++ b/Govor.Core/Infrastructure/Validators/IObjectValidator.cs @@ -6,4 +6,4 @@ public interface IObjectValidator bool TryValidate(T objectToValidate); } -class InvalidObjectException(Exception ex) : GovorCoreException($"The object {typeof(T).FullName} is invalid.", ex); \ No newline at end of file +public class InvalidObjectException(Exception ex) : GovorCoreException($"The object {typeof(T).FullName} is invalid.", ex); \ No newline at end of file diff --git a/Govor.Core/Infrastructure/Validators/UserValidator.cs b/Govor.Core/Infrastructure/Validators/UserValidator.cs index 3b0b9b9..d6c661b 100644 --- a/Govor.Core/Infrastructure/Validators/UserValidator.cs +++ b/Govor.Core/Infrastructure/Validators/UserValidator.cs @@ -5,6 +5,8 @@ namespace Govor.Core.Infrastructure.Validators; public class UserValidator : IObjectValidator { + public const int MIN_LENGHT_OF_NAME = 4; + public const int MAX_LENGHT_OF_NAME = 100; public void Validate(User user) { try @@ -13,9 +15,13 @@ public class UserValidator : IObjectValidator throw new ArgumentNullException(nameof(user)); if(user.Id == Guid.Empty) throw new ArgumentException("User ID cannot be empty", nameof(user.Id)); + if(user.Username is null + || user.Username.Length < MIN_LENGHT_OF_NAME + || user.Username.Length > MAX_LENGHT_OF_NAME) + throw new ArgumentException($"Username cannot be empty or less then {MIN_LENGHT_OF_NAME} chars or more then {MAX_LENGHT_OF_NAME}", nameof(user.Username)); if(user.HashPassword is null || user.HashPassword == string.Empty) throw new ArgumentException("Password cannot be empty", nameof(user.HashPassword)); - if(user.CreatedOn == DateTime.MinValue) + if(user.CreatedOn == DateOnly.MinValue) throw new ArgumentException("Time of creation account cannot be empty", nameof(user.CreatedOn)); } catch(Exception ex) diff --git a/Govor.Core/Models/User.cs b/Govor.Core/Models/User.cs index 5e548ae..43b109f 100644 --- a/Govor.Core/Models/User.cs +++ b/Govor.Core/Models/User.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Govor.Core.Models; public class User @@ -7,6 +9,6 @@ public class User public string Description {get; set;} public string HashPassword {get; set;} public Guid IconId {get; set;} - public DateTime CreatedOn {get; set;} + public DateOnly CreatedOn {get; set;} public DateTime WasOnline {get; set;} } \ No newline at end of file diff --git a/Govor.Core/Repositories/IUsersReader.cs b/Govor.Core/Repositories/IUsersReader.cs index e8ec196..3ff9d0d 100644 --- a/Govor.Core/Repositories/IUsersReader.cs +++ b/Govor.Core/Repositories/IUsersReader.cs @@ -7,7 +7,7 @@ public interface IUsersReader public Task> GetAll(); public Task FindById(Guid id); public Task> FindByRangeId(IEnumerable ids); - public Task> FindUsersByName(string username); - public Task> FindByRangeUsername(IEnumerable usernames); + public Task FindByUsername(string username); + public Task> FindByRangeUsernames(IEnumerable usernames); public Task> FindUsersByCreatedDate(DateOnly createdDate); } \ No newline at end of file diff --git a/Govor.Data/Repositories/UsersRepository.cs b/Govor.Data/Repositories/UsersRepository.cs index a0feee6..3da760e 100644 --- a/Govor.Data/Repositories/UsersRepository.cs +++ b/Govor.Data/Repositories/UsersRepository.cs @@ -22,11 +22,14 @@ public class UsersRepository : IUsersRepository return await _context.Users .AsNoTracking() .Where(x => true) - .ToListAsync(); + .ToListOrThrowIfEmpty(new NotFoundException("Users in Database not exists")); } public async Task FindById(Guid id) { + if(id == Guid.Empty) + throw new ArgumentException("Id must not be empty", nameof(id)); + return await _context.Users .AsNoTracking() .FirstOrDefaultAsync(x => x.Id == id) @@ -35,45 +38,101 @@ public class UsersRepository : IUsersRepository public async Task> FindByRangeId(IEnumerable ids) { + if (ids is null || !ids.Any()) + throw new ArgumentException("Ids must not be empty", nameof(ids)); + return await _context.Users .AsNoTracking() .Where(x => ids.Contains(x.Id)) .ToListOrThrowIfEmpty(new NotFoundByKeyException>(ids,"Users with given ids not found")); } - public async Task> FindUsersByName(string username) + public async Task FindByUsername(string username) { + if(username is null || username == string.Empty) + throw new ArgumentNullException(username, "Username cannot be empty"); + return await _context.Users .AsNoTracking() - .Where(x => x.Username == username) - .ToListOrThrowIfEmpty(new NotFoundByKeyException(username, "Users with given username not found")); + .FirstOrDefaultAsync(x => x.Username == username) + ?? throw new NotFoundByKeyException(username, "User with given username does not exist"); } - public async Task> FindByRangeUsername(IEnumerable usernames) + public async Task> FindByRangeUsernames(IEnumerable usernames) { + if (usernames is null || !usernames.Any()) + throw new ArgumentException("Usernames must not be empty", nameof(usernames)); + return await _context.Users .AsNoTracking() .Where(x => usernames.Contains(x.Username)) .ToListOrThrowIfEmpty(new NotFoundByKeyException>(usernames, "Users with given usernames not found")); } - public Task> FindUsersByCreatedDate(DateOnly createdDate) + public async Task> FindUsersByCreatedDate(DateOnly createdDate) { - throw new NotImplementedException(); + if(createdDate == DateOnly.MinValue) + throw new ArgumentException("Created date cannot be earlier than MinValue", nameof(createdDate)); + + return await _context.Users + .AsNoTracking() + .Where(u => createdDate == u.CreatedOn) + .ToListOrThrowIfEmpty(new NotFoundByKeyException(createdDate, "Users with given created date do not exist")); + } + + public async Task Add(User user) + { + try + { + _validator.Validate(user); + + _context.Users.Add(user); + + await _context.SaveChangesAsync(); + } + catch (InvalidObjectException ex) + { + throw new AdditionUserException("User with given data invalid", ex); + } + catch (Exception ex) + { + throw new AdditionUserException("Cannot add user", ex); + } } - public Task Add(User user) + public async Task Update(User user) { - throw new NotImplementedException(); - } + try + { + _validator.Validate(user); - public Task Update(User user) - { - throw new NotImplementedException(); + var rowsAffected = await _context.Users + .Where(u => u.Id == user.Id) + .ExecuteUpdateAsync(u => u + .SetProperty(a => a.Username, user.Username) + .SetProperty(u => u.IconId, user.IconId) + .SetProperty(u => u.Description, user.Description) + .SetProperty(u => u.CreatedOn, user.CreatedOn) + .SetProperty(u => u.HashPassword, user.HashPassword) + .SetProperty(u => u.WasOnline, user.WasOnline) + ); + + if (rowsAffected == 0) + throw new NotFoundByKeyException(user.Id); + } + catch (NotFoundByKeyException ex) + { + throw new UserUpdateException($"Not found user by given id {user.Id}", ex); + } + catch (Exception ex) + { + throw new UserUpdateException($"Error when updating the user {user.Id}", ex); + } } public Task Remove(User user) { + _validator.Validate(user); throw new NotImplementedException(); } @@ -84,6 +143,7 @@ public class UsersRepository : IUsersRepository public Task Exists(User user) { + _validator.Validate(user); throw new NotImplementedException(); } @@ -96,4 +156,11 @@ public class UsersRepository : IUsersRepository { throw new NotImplementedException(); } -} \ No newline at end of file +} + +public class AdditionUserException(string s, Exception ex) + : Exception(s, ex); + +public class UserUpdateException(string s, Exception ex) + : Exception(s, ex); + \ No newline at end of file