test project created
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user