Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using AutoMapper;
using Hng.Application.Features.UserManagement.Commands;
using Hng.Application.Features.UserManagement.Dtos;
using Hng.Application.Features.UserManagement.Handlers;
using Hng.Domain.Entities;
using Hng.Domain.Enums;
using Hng.Infrastructure.Repository.Interface;
using Microsoft.AspNetCore.Http;
using Moq;
using System;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Hng.Application.Tests.Features.UserManagement.Handlers;

public class UserActivationCommandHandlerTests
{
[Fact]
public async Task Handle_UserNotFound_ReturnsNotFoundResponse()
{
// Arrange
var mockUserRepository = new Mock<IRepository<User>>();
var mockMapper = new Mock<IMapper>();
var handler = new UserActivationCommandHandler(mockUserRepository.Object, mockMapper.Object);
var command = new UserActivationCommand { UserId = Guid.NewGuid() };

mockUserRepository.Setup(repo => repo.GetBySpec(It.IsAny<System.Linq.Expressions.Expression<Func<User, bool>>>()))
.ReturnsAsync((User)null);

// Act
var result = await handler.Handle(command, CancellationToken.None);

// Assert
Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode);
Assert.Equal("User not found.", result.Message);
}

[Fact]
public async Task Handle_UserAlreadyActive_ReturnsBadRequestResponse()
{
// Arrange
var userId = Guid.NewGuid();
var user = new User { Id = userId, UserStatus = UserStatus.activate };
var mockUserRepository = new Mock<IRepository<User>>();
var mockMapper = new Mock<IMapper>();
var handler = new UserActivationCommandHandler(mockUserRepository.Object, mockMapper.Object);
var command = new UserActivationCommand { UserId = userId };

mockUserRepository.Setup(repo => repo.GetBySpec(It.IsAny<System.Linq.Expressions.Expression<Func<User, bool>>>()))
.ReturnsAsync(user);

// Act
var result = await handler.Handle(command, CancellationToken.None);

// Assert
Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode);
Assert.Equal("User is already active.", result.Message);
}

[Fact]
public async Task Handle_UserActivationSuccess_ReturnsOkResponse()
{
// Arrange
var userId = Guid.NewGuid();
var user = new User { Id = userId, UserStatus = UserStatus.deactivate };
var userDto = new UserResponseDto { Id = userId.ToString() };
var mockUserRepository = new Mock<IRepository<User>>();
var mockMapper = new Mock<IMapper>();
var handler = new UserActivationCommandHandler(mockUserRepository.Object, mockMapper.Object);
var command = new UserActivationCommand { UserId = userId };

mockUserRepository.Setup(repo => repo.GetBySpec(It.IsAny<System.Linq.Expressions.Expression<Func<User, bool>>>()))
.ReturnsAsync(user);
mockUserRepository.Setup(repo => repo.UpdateAsync(It.IsAny<User>()))
.Returns(Task.CompletedTask);
mockUserRepository.Setup(repo => repo.SaveChanges())
.Returns(Task.CompletedTask);
mockMapper.Setup(mapper => mapper.Map<UserResponseDto>(It.IsAny<User>()))
.Returns(userDto);

// Act
var result = await handler.Handle(command, CancellationToken.None);

// Assert
Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
Assert.Equal("User activated successfully.", result.Message);
}

[Fact]
public async Task Handle_ExceptionThrown_ReturnsInternalServerError()
{
// Arrange
var userId = Guid.NewGuid();
var user = new User { Id = userId, UserStatus = UserStatus.deactivate };
var mockUserRepository = new Mock<IRepository<User>>();
var mockMapper = new Mock<IMapper>();
var handler = new UserActivationCommandHandler(mockUserRepository.Object, mockMapper.Object);
var command = new UserActivationCommand { UserId = userId };

mockUserRepository.Setup(repo => repo.GetBySpec(It.IsAny<System.Linq.Expressions.Expression<Func<User, bool>>>()))
.ReturnsAsync(user);
mockUserRepository.Setup(repo => repo.UpdateAsync(It.IsAny<User>()))
.ThrowsAsync(new Exception("Database error"));

// Act
var result = await handler.Handle(command, CancellationToken.None);

// Assert
Assert.Equal(StatusCodes.Status500InternalServerError, result.StatusCode);
Assert.Equal("An error occurred while activating the user.", result.Message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Hng.Application.Features.UserManagement.Dtos;
using MediatR;

namespace Hng.Application.Features.UserManagement.Commands
{
public class UserActivationCommand : IRequest<UserActivationResponse>
{
public Guid UserId { get; set; }
}
}
10 changes: 10 additions & 0 deletions src/Hng.Application/Features/UserManagement/Dtos/SignUpResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,14 @@ public class SignupResponseData
[JsonPropertyName("subscriptions")]
public List<SubscribeFreePlanResponse> Subscription { get; set; } = [];
}
public class UserActivationResponse
{
[JsonPropertyName("message")]
public string Message { get; set; }
public SignupResponseData Data { get; set; }
[JsonPropertyName("access_token")]
public string Token { get; set; }
[JsonPropertyName("status_code")]
public int StatusCode { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Hng.Application.Features.UserManagement.Commands;
using Hng.Application.Features.UserManagement.Dtos;
using Hng.Domain.Entities;
using Hng.Domain.Enums;
using Hng.Infrastructure.Repository.Interface;
using Hng.Infrastructure.Services.Interfaces;
using MediatR;
Expand Down Expand Up @@ -56,6 +57,16 @@ public async Task<UserLoginResponseDto<SignupResponseData>> Handle(CreateUserLog
StatusCode = StatusCodes.Status401Unauthorized
};
}
if (user.UserStatus == UserStatus.deactivate)
{
return new UserLoginResponseDto<SignupResponseData>
{
Data = null,
AccessToken = null,
Message = "User Deactivated",
StatusCode = StatusCodes.Status401Unauthorized
};
}

if (user.Status == "Inactive")
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using AutoMapper;
using Hng.Application.Features.UserManagement.Commands;
using Hng.Application.Features.UserManagement.Dtos;
using Hng.Domain.Entities;
using Hng.Domain.Enums;
using Hng.Infrastructure.Repository.Interface;
using MediatR;
using Microsoft.AspNetCore.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Hng.Application.Features.UserManagement.Handlers;

public class UserActivationCommandHandler : IRequestHandler<UserActivationCommand, UserActivationResponse>
{
private readonly IRepository<User> _userRepository;
private readonly IMapper _mapper;

public UserActivationCommandHandler(IRepository<User> userRepository, IMapper mapper)
{
_userRepository = userRepository;
_mapper = mapper;
}

public async Task<UserActivationResponse> Handle(UserActivationCommand request, CancellationToken cancellationToken)
{
var user = await _userRepository.GetBySpec(u => u.Id == request.UserId);

if (user is null)
{
return new UserActivationResponse
{
Message = "User not found.",
StatusCode = StatusCodes.Status404NotFound
};
}

if (user.UserStatus == UserStatus.activate)
{
return new UserActivationResponse
{
Message = "User is already active.",
StatusCode = StatusCodes.Status400BadRequest
};
}

user.UserStatus = UserStatus.activate;

try
{
await _userRepository.UpdateAsync(user);
await _userRepository.SaveChanges();

var userDto = _mapper.Map<UserResponseDto>(user);

return new UserActivationResponse
{
Message = "User activated successfully.",
StatusCode = StatusCodes.Status200OK,
};
}
catch (Exception ex)
{
// Log the exception
return new UserActivationResponse
{
Message = "An error occurred while activating the user.",
StatusCode = StatusCodes.Status500InternalServerError
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public async Task<SignUpResponse> Handle(UserSignUpCommand request, Cancellation

var createdUser = _mapper.Map<User>(request.SignUpBody);
createdUser.Id = Guid.NewGuid();
createdUser.UserStatus = UserStatus.activate;
(createdUser.PasswordSalt, createdUser.Password) = _passwordService.GeneratePasswordSaltAndHash(request.SignUpBody.Password);

var userOrg = new Organization
Expand Down
2 changes: 2 additions & 0 deletions src/Hng.Domain/Entities/User.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Hng.Domain.Enums;
using System.ComponentModel.DataAnnotations;

namespace Hng.Domain.Entities;
Expand Down Expand Up @@ -37,4 +38,5 @@ public class User : EntityBase
public Timezone Timezone { get; set; }
public Guid? LanguageId { get; set; }
public Language Language { get; set; }
public UserStatus UserStatus { get; set; }
}
8 changes: 8 additions & 0 deletions src/Hng.Domain/Enums/UserStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Hng.Domain.Enums
{
public enum UserStatus
{
activate,
deactivate
}
}
8 changes: 4 additions & 4 deletions src/Hng.Infrastructure/ConfigureInfrastructure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public static IServiceCollection AddInfrastructureConfig(this IServiceCollection
services.AddScoped<IOrganisationInviteService, OrganisationInviteService>();
services.AddScoped<IEmailTemplateService, EmailTemplateService>();
services.AddSingleton<IConnectionMultiplexer>(sp =>
{
var Configuration = ConfigurationOptions.Parse(redisConnectionString, true);
return ConnectionMultiplexer.Connect(Configuration);
});
{
var Configuration = ConfigurationOptions.Parse(redisConnectionString, true);
return ConnectionMultiplexer.Connect(Configuration);
});
return services;
}
}
Expand Down
Loading
Loading