Newer
Older
Warehouse / tests / Domain.UnitTests / Products / ProductTests.cs
@Derek Comartin Derek Comartin on 22 Aug 2023 8 KB Init
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);
        }
    }
}