From 99823621fa3aa773ed40f6ca8757b027a5d101af Mon Sep 17 00:00:00 2001 From: Nguyen Quang Chien Date: Mon, 29 May 2023 18:59:45 +0700 Subject: [PATCH 1/5] add: integration test --- .../Lockers/Commands/RemoveLockerTests.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs diff --git a/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs new file mode 100644 index 00000000..94c7d288 --- /dev/null +++ b/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs @@ -0,0 +1,90 @@ +using Application.Lockers.Commands; +using Application.Rooms.Commands; +using Domain.Entities; +using Domain.Entities.Physical; +using FluentAssertions; +using Xunit; + +namespace Application.Tests.Integration.Lockers.Commands; + +public class RemoveLockerTests : BaseClassFixture +{ + public RemoveLockerTests(CustomApiFactory apiFactory) : base(apiFactory) + { + } + + [Fact] + public async Task ShouldRemoveLocker_WhenLockerHasNoDocuments() + { + // Arrange + var department = CreateDepartment(); + var locker = CreateLocker(); + var room = CreateRoom(department, locker); + await Add(room); + + var command = new RemoveLocker.Command() + { + LockerId = locker.Id, + }; + + // Act + var result = await SendAsync(command); + + // Assert + result.Id.Should().Be(locker.Id); + var removedLocker = await FindAsync(room.Id); + removedLocker.Should().BeNull(); + + // Cleanup + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowConflictException_WhenRoomStillHasDocuments() + { + // Arrange + var department = CreateDepartment(); + var documents = CreateNDocuments(1); + var folder = CreateFolder(documents); + var locker = CreateLocker(folder); + var room = CreateRoom(department, locker); + await AddAsync(room); + + var command = new RemoveLocker.Command() + { + LockerId = locker.Id + }; + + // Act + var action = async () => await SendAsync(command); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("Locker cannot be removed because it contains documents."); + + // Cleanup + Remove(documents.First()); + Remove(folder); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowKeyNotFoundException_WhenLockerDoesNotExist() + { + // Arrange + var command = new RemoveLocker.Command() + { + LockerId = Guid.NewGuid() + }; + + // Act + var action = async () => await SendAsync(command); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("Locker does not exist."); + } +} \ No newline at end of file From 9c52f86eec3f8d9fce1b4f1225343330604ccba5 Mon Sep 17 00:00:00 2001 From: Nguyen Quang Chien Date: Mon, 29 May 2023 21:11:55 +0700 Subject: [PATCH 2/5] fix: something --- .../Lockers/Commands/RemoveLockerTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs index 94c7d288..b6c1820f 100644 --- a/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs +++ b/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs @@ -32,7 +32,7 @@ public async Task ShouldRemoveLocker_WhenLockerHasNoDocuments() // Assert result.Id.Should().Be(locker.Id); - var removedLocker = await FindAsync(room.Id); + var removedLocker = await FindAsync(locker.Id); removedLocker.Should().BeNull(); // Cleanup @@ -53,7 +53,7 @@ public async Task ShouldThrowConflictException_WhenRoomStillHasDocuments() var command = new RemoveLocker.Command() { - LockerId = locker.Id + LockerId = locker.Id, }; // Act @@ -77,7 +77,7 @@ public async Task ShouldThrowKeyNotFoundException_WhenLockerDoesNotExist() // Arrange var command = new RemoveLocker.Command() { - LockerId = Guid.NewGuid() + LockerId = Guid.NewGuid(), }; // Act From 4d429ae736f679c38a10a5ba3dbf9b944eed11f2 Mon Sep 17 00:00:00 2001 From: StarryFolf Date: Wed, 31 May 2023 10:34:11 +0700 Subject: [PATCH 3/5] feat: implement remove locker fix: refactoring --- .../Common/Models/Dtos/DepartmentDto.cs | 11 +++- .../Common/Models/Dtos/Physical/RoomDto.cs | 9 ++- .../Lockers/Commands/DisableLocker.cs | 4 +- .../Lockers/Commands/RemoveLocker.cs | 57 +++++++++++++++++++ src/Application/Rooms/Commands/AddRoom.cs | 2 + .../Lockers/Commands/RemoveLockerTests.cs | 2 +- 6 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/Application/Common/Models/Dtos/DepartmentDto.cs b/src/Application/Common/Models/Dtos/DepartmentDto.cs index 68d00db4..9ae2e4e6 100644 --- a/src/Application/Common/Models/Dtos/DepartmentDto.cs +++ b/src/Application/Common/Models/Dtos/DepartmentDto.cs @@ -1,5 +1,5 @@ using Application.Common.Mappings; -using Application.Common.Models.Dtos.Physical; +using AutoMapper; using Domain.Entities; namespace Application.Common.Models.Dtos; @@ -8,5 +8,12 @@ public class DepartmentDto : IMapFrom { public Guid Id { get; set; } public string Name { get; set; } = null!; - public RoomDto? Room { get; set; } + public Guid? RoomId { get; set; } + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(dest => dest.RoomId, + opt => opt.MapFrom(src => src.Room!.Id)); + } } \ No newline at end of file diff --git a/src/Application/Common/Models/Dtos/Physical/RoomDto.cs b/src/Application/Common/Models/Dtos/Physical/RoomDto.cs index a2ed7b95..44fe55e0 100644 --- a/src/Application/Common/Models/Dtos/Physical/RoomDto.cs +++ b/src/Application/Common/Models/Dtos/Physical/RoomDto.cs @@ -10,9 +10,16 @@ public class RoomDto : IMapFrom public Guid Id { get; set; } public string Name { get; set; } = null!; public string? Description { get; set; } - public StaffDto? Staff { get; set; } + public Guid? StaffId { get; set; } public DepartmentDto? Department { get; set; } public int Capacity { get; set; } public int NumberOfLockers { get; set; } public bool IsAvailable { get; set; } + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(dest => dest.StaffId, + opt => opt.MapFrom(src => src.Staff!.Id)); + } } \ No newline at end of file diff --git a/src/Application/Lockers/Commands/DisableLocker.cs b/src/Application/Lockers/Commands/DisableLocker.cs index eaa4f40b..b1c0339f 100644 --- a/src/Application/Lockers/Commands/DisableLocker.cs +++ b/src/Application/Lockers/Commands/DisableLocker.cs @@ -26,12 +26,12 @@ public record Command : IRequest public Guid LockerId { get; init; } } - public class RemoveLockerCommandHandler : IRequestHandler + public class DisableLockerCommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; - public RemoveLockerCommandHandler(IApplicationDbContext context, IMapper mapper) + public DisableLockerCommandHandler(IApplicationDbContext context, IMapper mapper) { _context = context; _mapper = mapper; diff --git a/src/Application/Lockers/Commands/RemoveLocker.cs b/src/Application/Lockers/Commands/RemoveLocker.cs index 11c6472b..2bb2a935 100644 --- a/src/Application/Lockers/Commands/RemoveLocker.cs +++ b/src/Application/Lockers/Commands/RemoveLocker.cs @@ -1,12 +1,69 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using FluentValidation; using MediatR; +using Microsoft.EntityFrameworkCore; namespace Application.Lockers.Commands; public class RemoveLocker { + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.LockerId) + .NotEmpty().WithMessage("LockerId is required."); + } + } + public record Command : IRequest { public Guid LockerId { get; init; } } + + public class RemoveLockerCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public RemoveLockerCommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var locker = await _context.Lockers + .Include(x => x.Room) + .FirstOrDefaultAsync(x => x.Id.Equals(request.LockerId), cancellationToken); + + if (locker is null) + { + throw new KeyNotFoundException("Locker does not exist."); + } + + var canNotRemove = await _context.Documents + .CountAsync(x => x.Folder!.Locker.Id.Equals(request.LockerId), cancellationToken) + > 0; + + if (canNotRemove) + { + throw new InvalidOperationException("Locker cannot be removed because it contains documents."); + } + + var room = locker.Room; + + var result = _context.Lockers.Remove(locker); + room.NumberOfLockers -= 1; + _context.Rooms.Update(room); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } } \ No newline at end of file diff --git a/src/Application/Rooms/Commands/AddRoom.cs b/src/Application/Rooms/Commands/AddRoom.cs index a317d7cf..13449b93 100644 --- a/src/Application/Rooms/Commands/AddRoom.cs +++ b/src/Application/Rooms/Commands/AddRoom.cs @@ -82,6 +82,8 @@ public async Task Handle(Command request, CancellationToken cancellatio NumberOfLockers = 0, Capacity = request.Capacity, Department = department, + DepartmentId = request.DepartmentId, + IsAvailable = true, }; var result = await _context.Rooms.AddAsync(entity, cancellationToken); await _context.SaveChangesAsync(cancellationToken); diff --git a/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs index b6c1820f..a5e19fcf 100644 --- a/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs +++ b/tests/Application.Tests.Integration/Lockers/Commands/RemoveLockerTests.cs @@ -36,7 +36,7 @@ public async Task ShouldRemoveLocker_WhenLockerHasNoDocuments() removedLocker.Should().BeNull(); // Cleanup - Remove(room); + Remove(await FindAsync(room.Id)); Remove(await FindAsync(department.Id)); } From 95de2daa53ea2b8c4392abe5b69c0b5949fab0e8 Mon Sep 17 00:00:00 2001 From: StarryFolf Date: Wed, 31 May 2023 11:40:34 +0700 Subject: [PATCH 4/5] refactoring --- src/Application/Rooms/Commands/DisableRoom.cs | 4 ++-- src/Application/Rooms/Commands/RemoveRoom.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Application/Rooms/Commands/DisableRoom.cs b/src/Application/Rooms/Commands/DisableRoom.cs index 580b62c6..dd9c7afe 100644 --- a/src/Application/Rooms/Commands/DisableRoom.cs +++ b/src/Application/Rooms/Commands/DisableRoom.cs @@ -26,12 +26,12 @@ public record Command : IRequest public Guid RoomId { get; init; } } - public class DisableRoomCommandHandler : IRequestHandler + public class CommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; - public DisableRoomCommandHandler(IApplicationDbContext context, IMapper mapper) + public CommandHandler(IApplicationDbContext context, IMapper mapper) { _context = context; _mapper = mapper; diff --git a/src/Application/Rooms/Commands/RemoveRoom.cs b/src/Application/Rooms/Commands/RemoveRoom.cs index ef079f47..9e35ddc1 100644 --- a/src/Application/Rooms/Commands/RemoveRoom.cs +++ b/src/Application/Rooms/Commands/RemoveRoom.cs @@ -25,12 +25,12 @@ public record Command : IRequest public Guid RoomId { get; init; } } - public class RemoveRoomCommandHandler : IRequestHandler + public class CommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; - public RemoveRoomCommandHandler(IApplicationDbContext context, IMapper mapper) + public CommandHandler(IApplicationDbContext context, IMapper mapper) { _context = context; _mapper = mapper; From da777fa95f2d95cc69ac4a564bcbc74f9c02730e Mon Sep 17 00:00:00 2001 From: StarryFolf Date: Wed, 31 May 2023 11:43:19 +0700 Subject: [PATCH 5/5] refactoring --- src/Application/Lockers/Commands/DisableLocker.cs | 4 ++-- src/Application/Lockers/Commands/RemoveLocker.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Application/Lockers/Commands/DisableLocker.cs b/src/Application/Lockers/Commands/DisableLocker.cs index b1c0339f..cbde208a 100644 --- a/src/Application/Lockers/Commands/DisableLocker.cs +++ b/src/Application/Lockers/Commands/DisableLocker.cs @@ -26,12 +26,12 @@ public record Command : IRequest public Guid LockerId { get; init; } } - public class DisableLockerCommandHandler : IRequestHandler + public class CommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; - public DisableLockerCommandHandler(IApplicationDbContext context, IMapper mapper) + public CommandHandler(IApplicationDbContext context, IMapper mapper) { _context = context; _mapper = mapper; diff --git a/src/Application/Lockers/Commands/RemoveLocker.cs b/src/Application/Lockers/Commands/RemoveLocker.cs index 2bb2a935..4a0ce1fc 100644 --- a/src/Application/Lockers/Commands/RemoveLocker.cs +++ b/src/Application/Lockers/Commands/RemoveLocker.cs @@ -26,12 +26,12 @@ public record Command : IRequest public Guid LockerId { get; init; } } - public class RemoveLockerCommandHandler : IRequestHandler + public class CommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; - public RemoveLockerCommandHandler(IApplicationDbContext context, IMapper mapper) + public CommandHandler(IApplicationDbContext context, IMapper mapper) { _context = context; _mapper = mapper;