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 _repo = new(); private readonly Mock _uow = new(); private readonly Mock _currentUser = new(); private readonly Mock _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 { ActiveOrder(storeA), ActiveOrder(storeB) }; _repo.Setup(r => r.GetAllAsync()).ReturnsAsync(orders); _repo.Setup(r => r.GetItemsByOrderIdAsync(It.IsAny())).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 { ActiveOrder(accessibleStore), ActiveOrder(restrictedStore) }; _repo.Setup(r => r.GetAllAsync()).ReturnsAsync(orders); _repo.Setup(r => r.GetItemsByOrderIdAsync(It.IsAny())).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())).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())).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())).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())).Returns(Task.CompletedTask); _repo.Setup(r => r.AddItemsAsync(It.IsAny>())).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())).ThrowsAsync(new Exception("db error")); await Assert.ThrowsAsync(() => _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())).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())).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); } }