using FluentAssertions; using MyWarehouse.Domain.Common.ValueObjects.Mass; using MyWarehouse.Domain.Common.ValueObjects.Money; using MyWarehouse.Domain.Exceptions; using MyWarehouse.Domain.Partners; using MyWarehouse.Domain.Products; using MyWarehouse.Domain.Transactions; using NUnit.Framework; using System; using System.Linq; using System.Reflection; namespace MyWarehouse.Domain.UnitTests.Products { public class ProductTests { [Test] public void NewProduct_ShouldThrow_WhenNameIsNullOrEmpty() { FluentActions.Invoking(() => new Product(null, "Description", new Money(1, Currency.USD), new Mass(1, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentException>("null").WithMessage("*name*"); FluentActions.Invoking(() => new Product("", "Description", new Money(1, Currency.USD), new Mass(1, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentException>("empty").WithMessage("*name*"); FluentActions.Invoking(() => new Product(" ", "Description", new Money(1, Currency.USD), new Mass(1, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentException>("whitespace").WithMessage("*name*"); } [Test] public void NewProduct_ShouldThrow_WhenDescriptionIsNullOrEmpty() { FluentActions.Invoking(() => new Product("Product", null, new Money(1, Currency.USD), new Mass(1, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentException>("null").WithMessage("*description*"); FluentActions.Invoking(() => new Product("Product", "", new Money(1, Currency.USD), new Mass(1, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentException>("empty").WithMessage("*description*"); FluentActions.Invoking(() => new Product("Product", " ", new Money(1, Currency.USD), new Mass(1, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentException>("whitespace").WithMessage("*description*"); } [Test] public void NewProduct_ShouldThrow_WhenPriceIsNull() { FluentActions.Invoking(() => new Product("Product", "Description", null, new Mass(1, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentNullException>("null").WithMessage("*price*"); } [Test] public void NewProduct_ShouldThrow_WhenPriceIsNotPositive() { FluentActions.Invoking(() => new Product("Product", "Description", new Money(0, Currency.USD), new Mass(1, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentException>().WithMessage("*price*"); } [Test] public void NewProduct_ShouldThrow_WhenMassIsNull() { FluentActions.Invoking(() => new Product("Product", "Description", new Money(1, Currency.USD), null)).Should() .ThrowExactly<ArgumentNullException>("null").WithMessage("*mass*"); } [Test] public void NewProduct_ShouldThrow_WhenMassIsNotPositive() { FluentActions.Invoking(() => new Product("Product", "Description", new Money(1, Currency.USD), new Mass(0, MassUnit.Kilogram))).Should() .ThrowExactly<ArgumentException>().WithMessage("*mass*"); } [Test] public void NewProduct_ShouldHavePropertiesAssigned() { var product = new Product("Product", "Description", new Money(1, Currency.USD), new Mass(2, MassUnit.Kilogram)); product.Name.Should().Be("Product"); product.Description.Should().Be("Description"); product.Price.Amount.Should().Be(1); product.Price.Currency.Should().Be(Currency.USD); product.Mass.Value.Should().Be(2); product.Mass.Unit.Should().Be(MassUnit.Kilogram); } [Test] public void NewProduct_IsCreatedWithZeroStock() { var product = new Product("Product", "Description", new Money(1, Currency.USD), new Mass(2, MassUnit.Kilogram)); product.NumberInStock.Should().Be(0, "new products must have empty stock, since stock must be generated by transactions"); } [Test] public void UpdateName_ShouldThrow_WhenNameIsNullOrEmpty() { var product = TestHelper.NewValidProduct_NoStock; FluentActions.Invoking(() => product.UpdateName(null)).Should() .ThrowExactly<ArgumentException>("null").WithMessage("*name*"); FluentActions.Invoking(() => product.UpdateName("")).Should() .ThrowExactly<ArgumentException>("empty").WithMessage("*name*"); FluentActions.Invoking(() => product.UpdateName(" ")).Should() .ThrowExactly<ArgumentException>("whitespace").WithMessage("*name*"); } [Test] public void UpdateDescription_ShouldThrow_WhenNameIsNullOrEmpty() { var product = TestHelper.NewValidProduct_NoStock; FluentActions.Invoking(() => product.UpdateDescription(null)).Should() .ThrowExactly<ArgumentException>("null").WithMessage("*description*"); FluentActions.Invoking(() => product.UpdateDescription("")).Should() .ThrowExactly<ArgumentException>("empty").WithMessage("*description*"); FluentActions.Invoking(() => product.UpdateDescription(" ")).Should() .ThrowExactly<ArgumentException>("whitespace").WithMessage("*description*"); } [Test] public void UpdatePrice_ShouldThrow_WhenValueIsNotPositive() { var product = TestHelper.NewValidProduct_NoStock; FluentActions.Invoking(() => product.UpdatePrice(0)).Should() .ThrowExactly<ArgumentException>().WithMessage("*price*"); } [Test] public void UpdateMass_ShouldThrow_WhenValueIsNotPositive() { var product = TestHelper.NewValidProduct_NoStock; FluentActions.Invoking(() => product.UpdateMass(0)).Should() .ThrowExactly<ArgumentException>().WithMessage("*mass*"); } [Test] public void RecordTransaction_ShouldThrow_WhenQuantityIsNotPositive() { var product = TestHelper.NewValidProduct_NoStock; Action act = () => product.RecordTransaction(new TransactionLine() { Transaction = TestHelper.NewTransaction_EmptyProcurement, Product = product, Quantity = 0, }); FluentActions.Invoking(act).Should() .ThrowExactly<ArgumentException>().WithMessage("*quantity*"); } [Test] public void RecordTransaction_WhenProcurement_IncreasesStock() { var product = TestHelper.NewValidProduct_NoStock; var initialStock = product.NumberInStock; product.RecordTransaction(new TransactionLine() { Transaction = TestHelper.NewTransaction_EmptyProcurement, Product = product, Quantity = 5, }); product.NumberInStock.Should().Be(initialStock + 5); } [Test] public void RecordTransaction_ShouldThrow_WhenSales_AndNoStock() { var product = TestHelper.NewValidProduct_NoStock; Action act = () => product.RecordTransaction(new TransactionLine() { Transaction = TestHelper.NewTransaction_EmptySales, Product = product, Quantity = 5, }); FluentActions.Invoking(act).Should() .ThrowExactly<InsufficientStockException>(); } [Test] public void RecordTransaction_WhenSales_DecreasesStock() { var product = TestHelper.NewValidProduct_WithStock(7); product.RecordTransaction(new TransactionLine() { Transaction = TestHelper.NewTransaction_EmptySales, Product = product, Quantity = 5, }); product.NumberInStock.Should().Be(7 - 5); } } }