230 lines
7.4 KiB
C#
230 lines
7.4 KiB
C#
using Distribution.Features.Order;
|
|
using Distribution.Infra.Authorization;
|
|
using Distribution.Infra.UoW;
|
|
using Distribution.Models;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Distribution.Tests.Services;
|
|
|
|
public class OrderServiceTests
|
|
{
|
|
private readonly Mock<IOrderRepository> _repo = new();
|
|
private readonly Mock<IUnitOfWork> _uow = new();
|
|
private readonly Mock<ICurrentUserService> _currentUser = new();
|
|
private readonly Mock<IScopeAccessService> _scopeAccess = new();
|
|
private readonly OrderService _sut;
|
|
|
|
public OrderServiceTests()
|
|
{
|
|
_sut = new OrderService(_repo.Object, _uow.Object, _currentUser.Object, _scopeAccess.Object);
|
|
}
|
|
|
|
private static Order ActiveOrder(Guid storeId) => new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
FK_StoreId = storeId,
|
|
OrderDate = DateTime.Now,
|
|
TotalAmount = 100,
|
|
IsActive = true
|
|
};
|
|
|
|
private void SetupGlobalUser(Guid userId)
|
|
{
|
|
_currentUser.Setup(u => u.UserId).Returns(userId);
|
|
_currentUser.Setup(u => u.RoleLevel).Returns(50);
|
|
}
|
|
|
|
private void SetupScopedUser(Guid userId)
|
|
{
|
|
_currentUser.Setup(u => u.UserId).Returns(userId);
|
|
_currentUser.Setup(u => u.RoleLevel).Returns(10);
|
|
}
|
|
|
|
// GetAllAsync
|
|
|
|
[Fact]
|
|
public async Task GetAllAsync_GlobalUser_ReturnsAllActiveOrders()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
var storeA = Guid.NewGuid();
|
|
var storeB = Guid.NewGuid();
|
|
SetupGlobalUser(userId);
|
|
var orders = new List<Order> { ActiveOrder(storeA), ActiveOrder(storeB) };
|
|
_repo.Setup(r => r.GetAllAsync()).ReturnsAsync(orders);
|
|
_repo.Setup(r => r.GetItemsByOrderIdAsync(It.IsAny<Guid>())).ReturnsAsync([]);
|
|
|
|
var result = await _sut.GetAllAsync();
|
|
|
|
Assert.Equal(2, result.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetAllAsync_ScopedUser_FiltersToAccessibleStores()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
var accessibleStore = Guid.NewGuid();
|
|
var restrictedStore = Guid.NewGuid();
|
|
SetupScopedUser(userId);
|
|
_scopeAccess.Setup(s => s.GetAccessibleStoreIdsAsync(userId)).ReturnsAsync([accessibleStore]);
|
|
var orders = new List<Order> { ActiveOrder(accessibleStore), ActiveOrder(restrictedStore) };
|
|
_repo.Setup(r => r.GetAllAsync()).ReturnsAsync(orders);
|
|
_repo.Setup(r => r.GetItemsByOrderIdAsync(It.IsAny<Guid>())).ReturnsAsync([]);
|
|
|
|
var result = await _sut.GetAllAsync();
|
|
|
|
Assert.Single(result);
|
|
Assert.Equal(accessibleStore, result[0].StoreId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetAllAsync_FiltersOutInactiveOrders()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
SetupGlobalUser(userId);
|
|
var inactiveOrder = ActiveOrder(Guid.NewGuid());
|
|
inactiveOrder.IsActive = false;
|
|
_repo.Setup(r => r.GetAllAsync()).ReturnsAsync([inactiveOrder]);
|
|
_repo.Setup(r => r.GetItemsByOrderIdAsync(It.IsAny<Guid>())).ReturnsAsync([]);
|
|
|
|
var result = await _sut.GetAllAsync();
|
|
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
// GetByIdAsync
|
|
|
|
[Fact]
|
|
public async Task GetByIdAsync_ReturnsNull_WhenOrderNotFound()
|
|
{
|
|
SetupGlobalUser(Guid.NewGuid());
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Order?)null);
|
|
|
|
var result = await _sut.GetByIdAsync(Guid.NewGuid());
|
|
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetByIdAsync_ReturnsNull_WhenUserLacksStoreScope()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
var storeId = Guid.NewGuid();
|
|
SetupScopedUser(userId);
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ActiveOrder(storeId));
|
|
_scopeAccess.Setup(s => s.HasStoreScopeAsync(userId, storeId)).ReturnsAsync(false);
|
|
|
|
var result = await _sut.GetByIdAsync(Guid.NewGuid());
|
|
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetByIdAsync_ReturnsOrder_WhenGlobalUser()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
var order = ActiveOrder(Guid.NewGuid());
|
|
SetupGlobalUser(userId);
|
|
_repo.Setup(r => r.GetByIdAsync(order.Id)).ReturnsAsync(order);
|
|
_repo.Setup(r => r.GetItemsByOrderIdAsync(order.Id)).ReturnsAsync([]);
|
|
|
|
var result = await _sut.GetByIdAsync(order.Id);
|
|
|
|
Assert.NotNull(result);
|
|
Assert.Equal(order.Id, result.Id);
|
|
}
|
|
|
|
// CreateAsync
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_ReturnsNull_WhenUserLacksStoreScope()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
var storeId = Guid.NewGuid();
|
|
SetupScopedUser(userId);
|
|
_scopeAccess.Setup(s => s.HasStoreScopeAsync(userId, storeId)).ReturnsAsync(false);
|
|
|
|
var result = await _sut.CreateAsync(
|
|
new CreateOrderRequest(storeId, DateTime.Now, 100, []));
|
|
|
|
Assert.Null(result);
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_CreatesOrder_WhenGlobalUser()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
var storeId = Guid.NewGuid();
|
|
SetupGlobalUser(userId);
|
|
_repo.Setup(r => r.AddAsync(It.IsAny<Order>())).Returns(Task.CompletedTask);
|
|
_repo.Setup(r => r.AddItemsAsync(It.IsAny<IEnumerable<OrderItem>>())).Returns(Task.CompletedTask);
|
|
|
|
var result = await _sut.CreateAsync(
|
|
new CreateOrderRequest(storeId, DateTime.Now, 250, []));
|
|
|
|
Assert.NotNull(result);
|
|
Assert.Equal(storeId, result.StoreId);
|
|
Assert.Equal(250, result.TotalAmount);
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Once);
|
|
_uow.Verify(u => u.CommitAsync(default), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_Rollbacks_OnException()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
SetupGlobalUser(userId);
|
|
_repo.Setup(r => r.AddAsync(It.IsAny<Order>())).ThrowsAsync(new Exception("db error"));
|
|
|
|
await Assert.ThrowsAsync<Exception>(() =>
|
|
_sut.CreateAsync(new CreateOrderRequest(Guid.NewGuid(), DateTime.Now, 100, [])));
|
|
|
|
_uow.Verify(u => u.RollbackAsync(default), Times.Once);
|
|
_uow.Verify(u => u.CommitAsync(default), Times.Never);
|
|
}
|
|
|
|
// DeleteAsync
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_ReturnsFalse_WhenOrderNotFound()
|
|
{
|
|
SetupGlobalUser(Guid.NewGuid());
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Order?)null);
|
|
|
|
var result = await _sut.DeleteAsync(Guid.NewGuid());
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_ReturnsFalse_WhenUserLacksStoreScope()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
var storeId = Guid.NewGuid();
|
|
SetupScopedUser(userId);
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ActiveOrder(storeId));
|
|
_scopeAccess.Setup(s => s.HasStoreScopeAsync(userId, storeId)).ReturnsAsync(false);
|
|
|
|
var result = await _sut.DeleteAsync(Guid.NewGuid());
|
|
|
|
Assert.False(result);
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_RemovesOrder_WhenAuthorized()
|
|
{
|
|
var userId = Guid.NewGuid();
|
|
var order = ActiveOrder(Guid.NewGuid());
|
|
SetupGlobalUser(userId);
|
|
_repo.Setup(r => r.GetByIdAsync(order.Id)).ReturnsAsync(order);
|
|
|
|
var result = await _sut.DeleteAsync(order.Id);
|
|
|
|
Assert.True(result);
|
|
_repo.Verify(r => r.Remove(order), Times.Once);
|
|
_uow.Verify(u => u.CommitAsync(default), Times.Once);
|
|
}
|
|
}
|