Newer
Older
Warehouse / tests / Application.IntegrationTests / Transactions / CreateTransactionTests.cs
@Derek Comartin Derek Comartin on 22 Aug 2023 6 KB Init
using FluentAssertions;
using MyWarehouse.Application.Common.Exceptions;
using MyWarehouse.Application.Transactions.CreateTransaction;
using MyWarehouse.Domain.Partners;
using MyWarehouse.Domain.Products;
using MyWarehouse.Domain.Transactions;
using NUnit.Framework;
using System.Linq;
using System.Threading.Tasks;


namespace MyWarehouse.Application.IntegrationTests.Transactions
{
    public class CreateTransactionTests : TestBase
    {
        [Test]
        public void WhenDtoIsBlank_ShouldThrow_InputValidationException()
        {
            var command = new CreateTransactionCommand();

            FluentActions.Invoking(() => TestFramework.SendAsync(command))
                .Should().ThrowExactlyAsync<InputValidationException>();
        }

        [Test]
        public async Task ProcurementRequest_ShouldCreateTransaction()
        {
            var partner = (await TestFramework.DataFactory.AddPartners(1)).First();
            var products = await TestFramework.DataFactory.AddProducts(3);
            var command = new CreateTransactionCommand()
            {
                PartnerId = partner.Id,
                TransactionType = Domain.TransactionType.Procurement,
                TransactionLines = products.Select(p => new CreateTransactionCommand.TransactionLine() {
                    ProductId = p.Id,
                    ProductQuantity = 5}).ToArray()
            };

            // Act.
            var result = await TestFramework.SendAsync(command);

            var transaction = await TestFramework.Data.FindAsync<Transaction>(result, x => x.TransactionLines, x => x.Partner);
            transaction.Should().NotBeNull();
            transaction.Partner.Id.Should().Be(partner.Id);
            transaction.Total.Amount.Should().Be(transaction.TransactionLines.Sum(x => x.UnitPrice.Amount * x.Quantity));
            transaction.TransactionLines.Should().HaveSameCount(products)
                .And.Subject.Select(x => new { x.ProductId, ProductQuantity = x.Quantity })
                .Should().BeEquivalentTo(command.TransactionLines, 
                    o => o.ComparingByMembers<CreateTransactionCommand.TransactionLine>());

            var newProducts = await TestFramework.Data.GetAllAsync<Product>();
            newProducts.Select(x => new { id = x.Id, qty = x.NumberInStock })
                .Should().BeEquivalentTo(command.TransactionLines
                .Select(x => new { id = x.ProductId, qty = x.ProductQuantity }));
        }

        [Test]
        public async Task WhenInvalidProductIdPresent_ShouldThrowException()
        {
            var partner = (await TestFramework.DataFactory.AddPartners(1)).First();
            var products = await TestFramework.DataFactory.AddProducts(3);
            var command = new CreateTransactionCommand()
            {
                PartnerId = partner.Id,
                TransactionType = Domain.TransactionType.Procurement,
                TransactionLines = products.Select(p => new CreateTransactionCommand.TransactionLine()
                {
                    ProductId = p.Id,
                    ProductQuantity = 5
                }).Concat(new CreateTransactionCommand.TransactionLine[] { new()
                {
                    ProductId = 62614, // Another line added with invalid product Id.
                    ProductQuantity = 2
                }
                })
                .ToArray()
            };

            (await FluentActions.Invoking(() => TestFramework.SendAsync(command))
                .Should().ThrowExactlyAsync<InputValidationException>())
                    .Which.Errors.Should().ContainKey("ProductId")
                    .And.Subject["ProductId"].Should().ContainMatch("*62614*");
        }

        [Test]
        public async Task SalesRequest_ShouldCreateTransaction()
        {
            CreateTransactionCommand.TransactionLine[] lines;
            Partner partner;
            
            {   // Create procurement transaction to prepare product stock for sales.
                var t = await TestFramework.DataFactory.CreateProcurementTransaction(3);
                lines = t.TransactionLines.Select(x => new CreateTransactionCommand.TransactionLine()
                {
                    ProductId = x.Product.Id,
                    ProductQuantity = x.Quantity
                }).ToArray();
                partner = t.Partner;
            }
            
            var command = new CreateTransactionCommand()
            {
                TransactionType = Domain.TransactionType.Sales,
                PartnerId = partner.Id,
                TransactionLines = lines
            };

            // Act.
            var result = await TestFramework.SendAsync(command);

            var transaction = await TestFramework.Data.FindAsync<Transaction>(result, x => x.TransactionLines, x => x.Partner);
            transaction.Should().NotBeNull();
            transaction.Partner.Id.Should().Be(partner.Id);
            transaction.Total.Amount.Should().Be(transaction.TransactionLines.Sum(x => x.UnitPrice.Amount * x.Quantity));
            transaction.TransactionLines.Should().HaveSameCount(lines)
                .And.Subject.Select(x => new { x.ProductId, ProductQuantity = x.Quantity })
                .Should().BeEquivalentTo(command.TransactionLines,
                    o => o.ComparingByMembers<CreateTransactionCommand.TransactionLine>());

            var products = await TestFramework.Data.GetAllAsync<Product>();
            products.Should().Match(x => x.All(p => p.NumberInStock == 0)); // Because we sold the exact amount we procured.
        }

        [Test]
        public async Task SalesRequest_WithoutStock_ShouldThrowException()
        {
            var partner = (await TestFramework.DataFactory.AddPartners(1)).First();
            var products = await TestFramework.DataFactory.AddProducts(2);

            var command = new CreateTransactionCommand()
            {
                TransactionType = Domain.TransactionType.Sales,
                PartnerId = partner.Id,
                TransactionLines = products.Select(p => new CreateTransactionCommand.TransactionLine()
                {
                    ProductId = p.Id,
                    ProductQuantity = 5
                }).ToArray()
            };

            (await FluentActions.Invoking(() => TestFramework.SendAsync(command))
                .Should().ThrowExactlyAsync<InputValidationException>())
                    .And.Errors.SelectMany(e => e.Value).Should().ContainMatch("*stock*");
        }
    }
}