260 lines
8.5 KiB
C#
260 lines
8.5 KiB
C#
using Distribution.Features.Item;
|
|
using Distribution.Infra.UoW;
|
|
using Distribution.Models;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Distribution.Tests.Services;
|
|
|
|
public class ItemServiceTests
|
|
{
|
|
private readonly Mock<IItemRepository> _repo = new();
|
|
private readonly Mock<IUnitOfWork> _uow = new();
|
|
private readonly ItemService _sut;
|
|
|
|
public ItemServiceTests()
|
|
{
|
|
_sut = new ItemService(_repo.Object, _uow.Object);
|
|
}
|
|
|
|
private static Item ManualItem(string sku = "SKU-001") => new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Sku = sku,
|
|
Name = "Test Item",
|
|
IsActive = true,
|
|
SyncSource = SyncSource.Manual,
|
|
FK_CategoryId = Guid.NewGuid(),
|
|
FK_BaseUomId = Guid.NewGuid(),
|
|
Category = new Category { Name = "Cat" },
|
|
BaseUom = new Uom { Name = "Each" },
|
|
Conversions = []
|
|
};
|
|
|
|
private static Item ErpItem() => new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Sku = "ERP-001",
|
|
Name = "Erp Item",
|
|
IsActive = true,
|
|
SyncSource = SyncSource.Erp,
|
|
FK_CategoryId = Guid.NewGuid(),
|
|
FK_BaseUomId = Guid.NewGuid(),
|
|
Category = new Category { Name = "Cat" },
|
|
BaseUom = new Uom { Name = "Each" },
|
|
Conversions = []
|
|
};
|
|
|
|
private static Item InactiveItem() => new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Sku = "OLD-001",
|
|
Name = "Old",
|
|
IsActive = false,
|
|
SyncSource = SyncSource.Manual,
|
|
FK_CategoryId = Guid.NewGuid(),
|
|
FK_BaseUomId = Guid.NewGuid(),
|
|
Category = new Category { Name = "Cat" },
|
|
BaseUom = new Uom { Name = "Each" },
|
|
Conversions = []
|
|
};
|
|
|
|
private static CreateItemRequest SampleCreateRequest(string sku = "SKU-001") =>
|
|
new(sku, "Test Item", null, Guid.NewGuid(), null, null, Guid.NewGuid(), null, null);
|
|
|
|
// CreateAsync
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_Throws_WhenSkuAlreadyExists()
|
|
{
|
|
_repo.Setup(r => r.SkuExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>())).ReturnsAsync(true);
|
|
|
|
await Assert.ThrowsAsync<InvalidOperationException>(
|
|
() => _sut.CreateAsync(SampleCreateRequest("DUP")));
|
|
|
|
_repo.Verify(r => r.AddAsync(It.IsAny<Item>()), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_SetsManualSyncSource()
|
|
{
|
|
var item = ManualItem();
|
|
_repo.Setup(r => r.SkuExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>())).ReturnsAsync(false);
|
|
_repo.Setup(r => r.AddAsync(It.IsAny<Item>())).Returns(Task.CompletedTask);
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(item);
|
|
|
|
await _sut.CreateAsync(SampleCreateRequest());
|
|
|
|
_repo.Verify(r => r.AddAsync(It.Is<Item>(i => i.SyncSource == SyncSource.Manual)), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_BeginsAndCommitsTransaction()
|
|
{
|
|
var item = ManualItem();
|
|
_repo.Setup(r => r.SkuExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>())).ReturnsAsync(false);
|
|
_repo.Setup(r => r.AddAsync(It.IsAny<Item>())).Returns(Task.CompletedTask);
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(item);
|
|
|
|
await _sut.CreateAsync(SampleCreateRequest());
|
|
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Once);
|
|
_uow.Verify(u => u.CommitAsync(default), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_Rollbacks_OnAddException()
|
|
{
|
|
_repo.Setup(r => r.SkuExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>())).ReturnsAsync(false);
|
|
_repo.Setup(r => r.AddAsync(It.IsAny<Item>())).ThrowsAsync(new Exception("db error"));
|
|
|
|
await Assert.ThrowsAsync<Exception>(() => _sut.CreateAsync(SampleCreateRequest()));
|
|
|
|
_uow.Verify(u => u.RollbackAsync(default), Times.Once);
|
|
_uow.Verify(u => u.CommitAsync(default), Times.Never);
|
|
}
|
|
|
|
// UpdateAsync
|
|
|
|
[Fact]
|
|
public async Task UpdateAsync_ReturnsNull_WhenItemNotFound()
|
|
{
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Item?)null);
|
|
|
|
var result = await _sut.UpdateAsync(Guid.NewGuid(),
|
|
new UpdateItemRequest("Name", null, Guid.NewGuid(), null, null, Guid.NewGuid(), null));
|
|
|
|
Assert.Null(result);
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateAsync_ReturnsNull_WhenItemInactive()
|
|
{
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveItem());
|
|
|
|
var result = await _sut.UpdateAsync(Guid.NewGuid(),
|
|
new UpdateItemRequest("Name", null, Guid.NewGuid(), null, null, Guid.NewGuid(), null));
|
|
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateAsync_ReturnsNull_WhenItemIsErpSynced()
|
|
{
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ErpItem());
|
|
|
|
var result = await _sut.UpdateAsync(Guid.NewGuid(),
|
|
new UpdateItemRequest("Name", null, Guid.NewGuid(), null, null, Guid.NewGuid(), null));
|
|
|
|
Assert.Null(result);
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateAsync_UpdatesFields_AndCommits()
|
|
{
|
|
var item = ManualItem();
|
|
_repo.Setup(r => r.GetByIdAsync(item.Id)).ReturnsAsync(item);
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(item);
|
|
|
|
var catId = Guid.NewGuid();
|
|
var uomId = Guid.NewGuid();
|
|
item.Category = new Category { Name = "Cat" };
|
|
item.BaseUom = new Uom { Name = "Each" };
|
|
|
|
var result = await _sut.UpdateAsync(item.Id,
|
|
new UpdateItemRequest("Updated", "desc", catId, null, null, uomId, 9.99m));
|
|
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Once);
|
|
_uow.Verify(u => u.CommitAsync(default), Times.Once);
|
|
}
|
|
|
|
// DeleteAsync
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_ReturnsFalse_WhenItemNotFound()
|
|
{
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Item?)null);
|
|
|
|
var result = await _sut.DeleteAsync(Guid.NewGuid());
|
|
|
|
Assert.False(result);
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_ReturnsFalse_WhenItemInactive()
|
|
{
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveItem());
|
|
|
|
var result = await _sut.DeleteAsync(Guid.NewGuid());
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_ReturnsFalse_WhenItemIsErpSynced()
|
|
{
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ErpItem());
|
|
|
|
var result = await _sut.DeleteAsync(Guid.NewGuid());
|
|
|
|
Assert.False(result);
|
|
_uow.Verify(u => u.BeginAsync(default), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_CallsRemove_AndCommits_ForManualItem()
|
|
{
|
|
var item = ManualItem();
|
|
_repo.Setup(r => r.GetByIdAsync(item.Id)).ReturnsAsync(item);
|
|
|
|
var result = await _sut.DeleteAsync(item.Id);
|
|
|
|
Assert.True(result);
|
|
_repo.Verify(r => r.Remove(item), Times.Once);
|
|
_uow.Verify(u => u.CommitAsync(default), Times.Once);
|
|
}
|
|
|
|
// ReplaceConversionsAsync
|
|
|
|
[Fact]
|
|
public async Task ReplaceConversionsAsync_ReturnsNull_WhenItemNotFound()
|
|
{
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Item?)null);
|
|
|
|
var result = await _sut.ReplaceConversionsAsync(Guid.NewGuid(),
|
|
new ReplaceConversionsRequest([]));
|
|
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReplaceConversionsAsync_ReturnsNull_WhenItemIsErpSynced()
|
|
{
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ErpItem());
|
|
|
|
var result = await _sut.ReplaceConversionsAsync(Guid.NewGuid(),
|
|
new ReplaceConversionsRequest([]));
|
|
|
|
Assert.Null(result);
|
|
_repo.Verify(r => r.ReplaceConversionsAsync(It.IsAny<Guid>(), It.IsAny<List<UomConversion>>()), Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReplaceConversionsAsync_CallsReplace_AndCommits()
|
|
{
|
|
var item = ManualItem();
|
|
_repo.Setup(r => r.GetByIdAsync(item.Id)).ReturnsAsync(item);
|
|
_repo.Setup(r => r.ReplaceConversionsAsync(It.IsAny<Guid>(), It.IsAny<List<UomConversion>>()))
|
|
.Returns(Task.CompletedTask);
|
|
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(item);
|
|
|
|
await _sut.ReplaceConversionsAsync(item.Id, new ReplaceConversionsRequest([]));
|
|
|
|
_repo.Verify(r => r.ReplaceConversionsAsync(item.Id, It.IsAny<List<UomConversion>>()), Times.Once);
|
|
_uow.Verify(u => u.CommitAsync(default), Times.Once);
|
|
}
|
|
}
|