using FluentAssertions; using foodmarket.Application.Inventory; using Xunit; namespace foodmarket.UnitTests; /// Скользящее средневзвешенное себестоимости (логика SuppliesController.Post). public class MovingAverageCostTests { [Fact] public void First_supply_with_no_stock_uses_purchase_price() { // нет остатка, нет себестоимости → берём цену закупки MovingAverageCost.Compute(currentQty: 0m, currentCost: 0m, incomingQty: 10m, incomingUnitPrice: 100m) .Should().Be(100m); } [Fact] public void Weighted_average_of_old_and_new() { // 10 шт по 100 + 10 шт по 200 → среднее 150 MovingAverageCost.Compute(currentQty: 10m, currentCost: 100m, incomingQty: 10m, incomingUnitPrice: 200m) .Should().Be(150m); } [Fact] public void Weighted_average_respects_quantities() { // 30 шт по 100 + 10 шт по 200 → (3000+2000)/40 = 125 MovingAverageCost.Compute(currentQty: 30m, currentCost: 100m, incomingQty: 10m, incomingUnitPrice: 200m) .Should().Be(125m); } [Fact] public void Result_is_rounded_to_four_decimals() { // (1*1 + 2*2)/3 = 1.6666... → 1.6667 MovingAverageCost.Compute(currentQty: 1m, currentCost: 1m, incomingQty: 2m, incomingUnitPrice: 2m) .Should().Be(1.6667m); } [Fact] public void Existing_stock_with_zero_cost_still_averages_when_qty_positive() { // currentCost==0 но currentQty>0 → НЕ берём цену закупки целиком, // считаем среднее: (5*0 + 5*200)/10 = 100 MovingAverageCost.Compute(currentQty: 5m, currentCost: 0m, incomingQty: 5m, incomingUnitPrice: 200m) .Should().Be(100m); } [Fact] public void Zero_total_quantity_falls_back_to_purchase_price() { // возврат/сторно может дать totalQty==0 → не делим на ноль MovingAverageCost.Compute(currentQty: -10m, currentCost: 50m, incomingQty: 10m, incomingUnitPrice: 70m) .Should().Be(70m); } }