From 5537e6e63d2951e4e9e050c777ada4fbaee0f925 Mon Sep 17 00:00:00 2001 From: Nguyen Quang Chien Date: Mon, 29 May 2023 19:17:00 +0700 Subject: [PATCH 1/7] add: integration test --- .../Lockers/Commands/UpdateLockerTests.cs | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs diff --git a/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs new file mode 100644 index 00000000..08ab96ea --- /dev/null +++ b/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs @@ -0,0 +1,127 @@ +using Application.Common.Exceptions; +using Application.Lockers.Commands; +using Domain.Entities; +using FluentAssertions; +using Xunit; + +namespace Application.Tests.Integration.Lockers.Commands; + +public class UpdateLockerTests : BaseClassFixture +{ + public UpdateLockerTests(CustomApiFactory apiFactory) : base(apiFactory) + { + } + + [Fact] + public async Task ShouldUpdateLocker_WhenUpdateDetailsAreValid() + { + // Arrange + var department = CreateDepartment(); + var locker = CreateLocker(); + var room = CreateRoom(department, locker); + await AddAsync(room); + + var command = new UpdateLocker.Command() + { + LockerId = locker.Id, + Name = "Something else", + Capacity = 6, + Description = "ehehe", + }; + + // Act + var result = await SendAsync(command); + + // Assert + result.Id.Should().Be(locker.Id); + result.Name.Should().Be(command.Name); + result.Capacity.Should().Be(command.Capacity); + result.Description.Should().Be(command.Description); + + // Cleanup + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowKeyNotFoundException_WhenThatLockerDoesNotExist() + { + // Arrange + var command = new UpdateLocker.Command() + { + LockerId = Guid.NewGuid(), + Name = "Something else", + Capacity = 6, + Description = "ehehe", + }; + + // Act + var result = async () => await SendAsync(command); + + // Assert + await result.Should().ThrowAsync() + .WithMessage("Locker does not exist"); + } + + [Fact] + public async Task ShouldThrowConflictException_WhenNewLockerNameHasAlreadyExistedInThatRoom() + { + // Arrange + var department = CreateDepartment(); + var duplicateNameLocker = CreateLocker(); + var locker = CreateLocker(); + var room = CreateRoom(department, duplicateNameLocker, locker); + await AddAsync(room); + + var command = new UpdateLocker.Command() + { + LockerId = locker.Id, + Name = duplicateNameLocker.Name, + Capacity = 6, + Description = "ehehe", + }; + + // Act + var result = async () => await SendAsync(command); + + // Assert + await result.Should().ThrowAsync() + .WithMessage("New locker name already exists."); + } + + [Fact] + public async Task ShouldThrowConflictException_WhenNewCapacityIsLessThanCurrentNumberOfFolders() + { + // Arrange + var department = CreateDepartment(); + var folder1 = CreateFolder(); + var folder2 = CreateFolder(); + var locker = CreateLocker(folder1, folder2); + locker.Capacity = 3; + var room = CreateRoom(department, locker); + await AddAsync(room); + + var command = new UpdateLocker.Command() + { + LockerId = locker.Id, + Name = "Something else", + Capacity = 1, + Description = "ehehe", + }; + + // Act + var result = async () => await SendAsync(command); + + // Assert + await result.Should().ThrowAsync() + .WithMessage("New capacity cannot be less than current number of folders."); + + // Cleanup + Remove(folder1); + Remove(folder2); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); + } +} \ No newline at end of file From a7b48f3b02957d0e7d0a01010662fd999fec6042 Mon Sep 17 00:00:00 2001 From: StarryFolf Date: Wed, 31 May 2023 13:54:48 +0700 Subject: [PATCH 2/7] feat: implement update locker DOT IN TEST EXCEPTION MESSAGE, CHIEN --- .../Lockers/Commands/UpdateLocker.cs | 68 +++++++++++++++++++ .../Lockers/Commands/UpdateLockerTests.cs | 2 +- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/Application/Lockers/Commands/UpdateLocker.cs b/src/Application/Lockers/Commands/UpdateLocker.cs index 580c1e6f..b88ea774 100644 --- a/src/Application/Lockers/Commands/UpdateLocker.cs +++ b/src/Application/Lockers/Commands/UpdateLocker.cs @@ -1,10 +1,34 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using Domain.Entities.Physical; +using Domain.Exceptions; +using FluentValidation; using MediatR; +using Microsoft.EntityFrameworkCore; namespace Application.Lockers.Commands; public class UpdateLocker { + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.Name) + .NotEmpty() + .MaximumLength(64).WithMessage("Locker's name cannot exceed 64 characters."); + + RuleFor(x => x.Description) + .MaximumLength(256).WithMessage("Locker's description cannot exceed 256 characters."); + + RuleFor(x => x.Capacity) + .GreaterThan(0).WithMessage("Locker's capacity cannot be less than 1"); + } + } public record Command : IRequest { public Guid LockerId { get; init; } @@ -12,4 +36,48 @@ public record Command : IRequest public string? Description { get; init; } public int Capacity { get; init; } } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var locker = await _context.Lockers.FirstOrDefaultAsync( + x => x.Id.Equals(request.LockerId), cancellationToken); + + var duplicateLocker = await _context.Lockers.FirstOrDefaultAsync( + x => x.Name.Equals(request.Name), cancellationToken); + + if (locker is null) + { + throw new KeyNotFoundException("Locker does not exist."); + } + + if (duplicateLocker is not null && !duplicateLocker.Equals(locker)) + { + throw new ConflictException("New locker name already exists."); + } + + if (locker.NumberOfFolders > request.Capacity) + { + throw new ConflictException("New capacity cannot be less than current number of folders."); + } + + locker.Name = request.Name; + locker.Description = request.Description; + locker.Capacity = request.Capacity; + + var result = _context.Lockers.Update(locker); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs index 08ab96ea..fbca607a 100644 --- a/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs +++ b/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs @@ -61,7 +61,7 @@ public async Task ShouldThrowKeyNotFoundException_WhenThatLockerDoesNotExist() // Assert await result.Should().ThrowAsync() - .WithMessage("Locker does not exist"); + .WithMessage("Locker does not exist."); } [Fact] From da86dd3b980907cd32080a59636353ff943b1c2a Mon Sep 17 00:00:00 2001 From: StarryFolf <67864500+StarryFolf@users.noreply.github.com> Date: Wed, 31 May 2023 17:47:53 +0700 Subject: [PATCH 3/7] Update GetAllDepartments.cs (temporary workaround until chien provides a solution) --- src/Application/Departments/Queries/GetAllDepartments.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Application/Departments/Queries/GetAllDepartments.cs b/src/Application/Departments/Queries/GetAllDepartments.cs index a4e5d711..3dfa3526 100644 --- a/src/Application/Departments/Queries/GetAllDepartments.cs +++ b/src/Application/Departments/Queries/GetAllDepartments.cs @@ -24,9 +24,11 @@ public QueryHandler(IApplicationDbContext context, IMapper mapper) public async Task> Handle(Query request, CancellationToken cancellationToken) { - var departments = await _context.Departments.ToListAsync(cancellationToken); + var departments = await _context.Departments + .Where(x => !x.Name.Equals("Admin")) + .ToListAsync(cancellationToken); var result = new ReadOnlyCollection(_mapper.Map>(departments)); return result; } } -} \ No newline at end of file +} From 19c6f6ddf20aa979906ad6f3384881b65eaa1357 Mon Sep 17 00:00:00 2001 From: StarryFolf Date: Wed, 31 May 2023 20:33:21 +0700 Subject: [PATCH 4/7] =?UTF-8?q?fuck=20chi=E1=BA=BFn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Lockers/Commands/UpdateLockerTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs index fbca607a..67031617 100644 --- a/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs +++ b/tests/Application.Tests.Integration/Lockers/Commands/UpdateLockerTests.cs @@ -88,6 +88,11 @@ public async Task ShouldThrowConflictException_WhenNewLockerNameHasAlreadyExiste // Assert await result.Should().ThrowAsync() .WithMessage("New locker name already exists."); + + // Cleanup + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] From 4ddd4c04fd8eeef85bd2d31fabf5791d245bc4e7 Mon Sep 17 00:00:00 2001 From: StarryFolf <67864500+StarryFolf@users.noreply.github.com> Date: Wed, 31 May 2023 23:26:21 +0700 Subject: [PATCH 5/7] Update UpdateLocker.cs --- src/Application/Lockers/Commands/UpdateLocker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Application/Lockers/Commands/UpdateLocker.cs b/src/Application/Lockers/Commands/UpdateLocker.cs index b88ea774..f389c468 100644 --- a/src/Application/Lockers/Commands/UpdateLocker.cs +++ b/src/Application/Lockers/Commands/UpdateLocker.cs @@ -53,14 +53,14 @@ public async Task Handle(Command request, CancellationToken cancellat var locker = await _context.Lockers.FirstOrDefaultAsync( x => x.Id.Equals(request.LockerId), cancellationToken); - var duplicateLocker = await _context.Lockers.FirstOrDefaultAsync( - x => x.Name.Equals(request.Name), cancellationToken); - if (locker is null) { throw new KeyNotFoundException("Locker does not exist."); } + var duplicateLocker = await _context.Lockers.FirstOrDefaultAsync( + x => x.Name.Equals(request.Name), cancellationToken); + if (duplicateLocker is not null && !duplicateLocker.Equals(locker)) { throw new ConflictException("New locker name already exists."); From 1a2c4dfb0f6f371d3d6f1b980a949f2b2ba58fd1 Mon Sep 17 00:00:00 2001 From: Nguyen Quang Chien Date: Wed, 31 May 2023 23:39:25 +0700 Subject: [PATCH 6/7] test --- src/Application/Departments/Queries/GetAllDepartments.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application/Departments/Queries/GetAllDepartments.cs b/src/Application/Departments/Queries/GetAllDepartments.cs index 3dfa3526..3a3a87ce 100644 --- a/src/Application/Departments/Queries/GetAllDepartments.cs +++ b/src/Application/Departments/Queries/GetAllDepartments.cs @@ -25,7 +25,7 @@ public QueryHandler(IApplicationDbContext context, IMapper mapper) public async Task> Handle(Query request, CancellationToken cancellationToken) { var departments = await _context.Departments - .Where(x => !x.Name.Equals("Admin")) + // .Where(x => !x.Name.Equals("Admin")) .ToListAsync(cancellationToken); var result = new ReadOnlyCollection(_mapper.Map>(departments)); return result; From 0a57fed8b2e406271c4786ab5321a4b222c31265 Mon Sep 17 00:00:00 2001 From: Nguyen Quang Chien Date: Wed, 31 May 2023 23:41:45 +0700 Subject: [PATCH 7/7] remove: exclude admin department --- src/Application/Departments/Queries/GetAllDepartments.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Application/Departments/Queries/GetAllDepartments.cs b/src/Application/Departments/Queries/GetAllDepartments.cs index 3a3a87ce..1a2f85c1 100644 --- a/src/Application/Departments/Queries/GetAllDepartments.cs +++ b/src/Application/Departments/Queries/GetAllDepartments.cs @@ -25,7 +25,6 @@ public QueryHandler(IApplicationDbContext context, IMapper mapper) public async Task> Handle(Query request, CancellationToken cancellationToken) { var departments = await _context.Departments - // .Where(x => !x.Name.Equals("Admin")) .ToListAsync(cancellationToken); var result = new ReadOnlyCollection(_mapper.Map>(departments)); return result;