From 721ffb026866f5efcbae89dd3f863918102a158b Mon Sep 17 00:00:00 2001 From: Nguyen Quang Chien Date: Mon, 29 May 2023 17:53:03 +0700 Subject: [PATCH 1/6] add: integration test --- src/Application/Rooms/Commands/UpdateRoom.cs | 2 +- .../Rooms/Commands/UpdateRoomTests.cs | 127 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs diff --git a/src/Application/Rooms/Commands/UpdateRoom.cs b/src/Application/Rooms/Commands/UpdateRoom.cs index 3dc20417..96533c4f 100644 --- a/src/Application/Rooms/Commands/UpdateRoom.cs +++ b/src/Application/Rooms/Commands/UpdateRoom.cs @@ -8,7 +8,7 @@ public class UpdateRoom public record Command : IRequest { public Guid RoomId { get; init; } - public string Name { get; set; } = null!; + public string Name { get; init; } = null!; public string? Description { get; init; } public int Capacity { get; init; } } diff --git a/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs b/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs new file mode 100644 index 00000000..8088e62d --- /dev/null +++ b/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs @@ -0,0 +1,127 @@ +using Application.Common.Exceptions; +using Application.Rooms.Commands; +using Domain.Entities; +using FluentAssertions; +using Xunit; + +namespace Application.Tests.Integration.Rooms.Commands; + +public class UpdateRoomTests : BaseClassFixture +{ + public UpdateRoomTests(CustomApiFactory apiFactory) : base(apiFactory) + { + } + + [Fact] + public async Task ShouldUpdateRoom_WhenUpdateDetailsAreValid() + { + // Act + var department = CreateDepartment(); + var room = CreateRoom(department); + await AddAsync(room); + + var command = new UpdateRoom.Command() + { + RoomId = room.Id, + Name = "Something else", + Description = "Description else", + Capacity = 6, + }; + + // Act + var result = await SendAsync(command); + + // Assert + result.Name.Should().Be(command.Name); + result.Description.Should().Be(command.Description); + result.Capacity.Should().Be(command.Capacity); + + // Cleanup + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowKeyNotFoundException_WhenRoomDoesNotExist() + { + // Act + var command = new UpdateRoom.Command() + { + RoomId = Guid.NewGuid(), + Name = "Something else", + Description = "Description else", + Capacity = 6, + }; + + // Act + var action = async () => await SendAsync(command); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("Room does not exist."); + } + + [Fact] + public async Task ShouldThrowConflictException_WhenNewCapacityIsLowerThanNumberOfCurrentLockers() + { + // Act + var department = CreateDepartment(); + var locker1 = CreateLocker(); + var locker2 = CreateLocker(); + var room = CreateRoom(department, locker1, locker2); + await AddAsync(room); + + var command = new UpdateRoom.Command() + { + RoomId = room.Id, + Name = "Something else", + Description = "Description else", + Capacity = 1, + }; + + // Act + var action = async () => await SendAsync(command); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("New capacity cannot be less than current number of lockers"); + + // Cleanup + Remove(locker1); + Remove(locker2); + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowConflictException_WhenNewNameHasAlreadyExisted() + { + // Act + var department1 = CreateDepartment(); + var department2 = CreateDepartment(); + var existedNameRoom = CreateRoom(department1); + var room = CreateRoom(department2); + await AddAsync(room); + + var command = new UpdateRoom.Command() + { + RoomId = room.Id, + Name = existedNameRoom.Name, + Description = "Description else", + Capacity = 1, + }; + + // Act + var action = async () => await SendAsync(command); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("New name has already existed."); + + // Cleanup + Remove(existedNameRoom); + Remove(room); + Remove(await FindAsync(department1.Id)); + Remove(await FindAsync(department2.Id)); + } +} \ No newline at end of file From 1a6b7a8af834c0b743d74e35d51cf036b813eb8a Mon Sep 17 00:00:00 2001 From: Nguyen Quang Chien Date: Mon, 29 May 2023 17:53:44 +0700 Subject: [PATCH 2/6] update: error message in test --- .../Rooms/Commands/UpdateRoomTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs b/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs index 8088e62d..70e78fa0 100644 --- a/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs +++ b/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs @@ -116,7 +116,7 @@ public async Task ShouldThrowConflictException_WhenNewNameHasAlreadyExisted() // Assert await action.Should().ThrowAsync() - .WithMessage("New name has already existed."); + .WithMessage("New name has already exists."); // Cleanup Remove(existedNameRoom); From a445c6057280e79fcf38c5cd2efc53a1c458948d Mon Sep 17 00:00:00 2001 From: Vzart Date: Tue, 30 May 2023 11:18:50 +0700 Subject: [PATCH 3/6] feat: update a room --- src/Application/Rooms/Commands/UpdateRoom.cs | 64 +++++++++++++++++++ .../Rooms/Commands/UpdateRoomTests.cs | 3 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/Application/Rooms/Commands/UpdateRoom.cs b/src/Application/Rooms/Commands/UpdateRoom.cs index 96533c4f..d8fb6153 100644 --- a/src/Application/Rooms/Commands/UpdateRoom.cs +++ b/src/Application/Rooms/Commands/UpdateRoom.cs @@ -1,5 +1,10 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using Domain.Entities.Physical; using MediatR; +using Microsoft.EntityFrameworkCore; namespace Application.Rooms.Commands; @@ -12,4 +17,63 @@ 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 room = await _context.Rooms + .FirstOrDefaultAsync(x => x.Id.Equals(request.RoomId), cancellationToken: cancellationToken); + + if (room is null) + { + throw new KeyNotFoundException("Room does not exist."); + } + + var nameExisted = await _context.Rooms.AnyAsync(x => x.Name + .ToLower().Equals(request.Name.ToLower()) + && x.Id != room.Id + , cancellationToken: cancellationToken); + + if (nameExisted) + { + throw new ConflictException("New name has already exists."); + } + + if (request.Capacity < room.NumberOfLockers) + { + throw new ConflictException("New capacity cannot be less than current number of lockers."); + } + + var updatedRoom = new Room + { + Id = room.Id, + Name = request.Name, + Description = request.Description, + Staff = room.Staff, + Department = room.Department, + DepartmentId = room.DepartmentId, + Capacity = request.Capacity, + NumberOfLockers = room.NumberOfLockers, + IsAvailable = room.IsAvailable, + Lockers = room.Lockers + }; + + _context.Rooms.Entry(room).State = EntityState.Detached; + _context.Rooms.Entry(updatedRoom).State = EntityState.Modified; + + await _context.SaveChangesAsync(cancellationToken); + + return _mapper.Map(updatedRoom); + } + } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs b/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs index 70e78fa0..060c1e7c 100644 --- a/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs +++ b/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs @@ -84,7 +84,7 @@ public async Task ShouldThrowConflictException_WhenNewCapacityIsLowerThanNumberO // Assert await action.Should().ThrowAsync() - .WithMessage("New capacity cannot be less than current number of lockers"); + .WithMessage("New capacity cannot be less than current number of lockers."); // Cleanup Remove(locker1); @@ -101,6 +101,7 @@ public async Task ShouldThrowConflictException_WhenNewNameHasAlreadyExisted() var department2 = CreateDepartment(); var existedNameRoom = CreateRoom(department1); var room = CreateRoom(department2); + await AddAsync(existedNameRoom); await AddAsync(room); var command = new UpdateRoom.Command() From dcfd4ce9b42dc384a4620d204a8ecf3466d8e244 Mon Sep 17 00:00:00 2001 From: Vzart Date: Thu, 1 Jun 2023 19:16:18 +0700 Subject: [PATCH 4/6] refactor: add validator and fix grammar --- src/Application/Rooms/Commands/UpdateRoom.cs | 22 ++++++++++++++++++- .../Rooms/Commands/UpdateRoomTests.cs | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Application/Rooms/Commands/UpdateRoom.cs b/src/Application/Rooms/Commands/UpdateRoom.cs index d8fb6153..f908dfe6 100644 --- a/src/Application/Rooms/Commands/UpdateRoom.cs +++ b/src/Application/Rooms/Commands/UpdateRoom.cs @@ -3,6 +3,7 @@ using Application.Common.Models.Dtos.Physical; using AutoMapper; using Domain.Entities.Physical; +using FluentValidation; using MediatR; using Microsoft.EntityFrameworkCore; @@ -10,6 +11,25 @@ namespace Application.Rooms.Commands; public class UpdateRoom { + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name can not be empty."); + + RuleFor(x => x.Name) + .MaximumLength(64).WithMessage("Name can not exceed 64 characters."); + + RuleFor(x => x.Capacity) + .NotEmpty().WithMessage("Capacity can not be empty"); + + RuleFor(x => x.Description) + .MaximumLength(256).WithMessage("Description can not exceed 256 characters."); + } + } public record Command : IRequest { public Guid RoomId { get; init; } @@ -46,7 +66,7 @@ public async Task Handle(Command request, CancellationToken cancellatio if (nameExisted) { - throw new ConflictException("New name has already exists."); + throw new ConflictException("Name has already exists."); } if (request.Capacity < room.NumberOfLockers) diff --git a/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs b/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs index 060c1e7c..25f0b5b1 100644 --- a/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs +++ b/tests/Application.Tests.Integration/Rooms/Commands/UpdateRoomTests.cs @@ -117,7 +117,7 @@ public async Task ShouldThrowConflictException_WhenNewNameHasAlreadyExisted() // Assert await action.Should().ThrowAsync() - .WithMessage("New name has already exists."); + .WithMessage("Name has already exists."); // Cleanup Remove(existedNameRoom); From 65105de5610d7353ac88fc924744dd33ca3cdd81 Mon Sep 17 00:00:00 2001 From: Vzart Date: Thu, 1 Jun 2023 19:19:15 +0700 Subject: [PATCH 5/6] fix: my soul --- src/Application/Rooms/Commands/UpdateRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application/Rooms/Commands/UpdateRoom.cs b/src/Application/Rooms/Commands/UpdateRoom.cs index f908dfe6..dbf7f442 100644 --- a/src/Application/Rooms/Commands/UpdateRoom.cs +++ b/src/Application/Rooms/Commands/UpdateRoom.cs @@ -24,7 +24,7 @@ public Validator() .MaximumLength(64).WithMessage("Name can not exceed 64 characters."); RuleFor(x => x.Capacity) - .NotEmpty().WithMessage("Capacity can not be empty"); + .NotEmpty().WithMessage("Capacity can not be empty."); RuleFor(x => x.Description) .MaximumLength(256).WithMessage("Description can not exceed 256 characters."); From 8bf4780a41951606dd12ae31b7d92813c7846437 Mon Sep 17 00:00:00 2001 From: Vzart Date: Thu, 1 Jun 2023 19:49:22 +0700 Subject: [PATCH 6/6] refactor: validator refactor to fit with project convention --- src/Application/Rooms/Commands/UpdateRoom.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Application/Rooms/Commands/UpdateRoom.cs b/src/Application/Rooms/Commands/UpdateRoom.cs index dbf7f442..ade660fe 100644 --- a/src/Application/Rooms/Commands/UpdateRoom.cs +++ b/src/Application/Rooms/Commands/UpdateRoom.cs @@ -18,9 +18,7 @@ public Validator() RuleLevelCascadeMode = CascadeMode.Stop; RuleFor(x => x.Name) - .NotEmpty().WithMessage("Name can not be empty."); - - RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name can not be empty.") .MaximumLength(64).WithMessage("Name can not exceed 64 characters."); RuleFor(x => x.Capacity)