Сделал 5 лабораторную

This commit is contained in:
2025-04-18 15:48:45 +04:00
parent 79f15f6e16
commit 93f0c94d40
15 changed files with 558 additions and 227 deletions

View File

@@ -1,18 +1,22 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using TwoFromTheCasketContracts.BusinessLogicsContracts;
using TwoFromTheCasketContracts.DataModels;
using TwoFromTheCasketContracts.Exceptions;
using TwoFromTheCasketContracts.Infastructure;
using TwoFromTheCasketContracts.Infastructure.SalaryConfiguration;
using TwoFromTheCasketContracts.StorageContracts;
namespace TwoFromTheCasketBusinessLogic.Implementation;
internal class SalaryBusinessLogicContract(ISalaryStorageContract _salaryStorageContract, IWorkerStorageContract _workerStorageContract,
IComplitedWorkStorageContract _сomplitedWorkStorageContract, ISpecializationStorageContract _specializationStorageContract, ILogger _logger) : ISalaryBusinessLogicContract
IComplitedWorkStorageContract _сomplitedWorkStorageContract, ISpecializationStorageContract _specializationStorageContract, ILogger _logger, IConfigurationSalary configuration) : ISalaryBusinessLogicContract
{
private ISalaryStorageContract salaryStorageContract = _salaryStorageContract;
private IWorkerStorageContract workerStorageContract = _workerStorageContract;
private IComplitedWorkStorageContract сomplitedWorkStorageContract = _сomplitedWorkStorageContract;
private ISpecializationStorageContract specializationStorageContract = _specializationStorageContract;
private IConfigurationSalary configurationSalary = configuration;
private ILogger logger = _logger;
public List<SalaryDataModel> GetSalariesByWorkerByPeriod(string workerId, DateTime fromDate, DateTime toDate)
@@ -47,56 +51,118 @@ internal class SalaryBusinessLogicContract(ISalaryStorageContract _salaryStorage
public void CalculateSalaryByMonth(DateTime date)
{
_logger.LogInformation("Calculating salary for month: {Date}", date);
logger.LogInformation("Calculating salary for month: {Date}", date);
var startDate = new DateTime(date.Year, date.Month, 1, 0, 0, 0, DateTimeKind.Utc);
var finishDate = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month), 23, 59, 59, DateTimeKind.Utc);
var finishDate = new DateTime(
date.Year,
date.Month,
DateTime.DaysInMonth(date.Year, date.Month),
23, 59, 59,
DateTimeKind.Utc);
var workers = _workerStorageContract.GetList() ?? throw new StorageException(new InvalidOperationException("Failed to retrieve workerStorageContract from storage."));
var workers = workerStorageContract.GetList()
?? throw new StorageException(new InvalidOperationException("Failed to load workers"));
foreach (var worker in workers)
{
var completedWorks = сomplitedWorkStorageContract.GetList(startDate, finishDate, WorkerId: worker.Id);
var completed = сomplitedWorkStorageContract
.GetList(startDate, finishDate, worker.Id)
?? throw new StorageException(new InvalidOperationException("Failed to load completed works"));
if (completedWorks == null)
throw new StorageException(new InvalidOperationException("Failed to retrieve completed works from storage."));
var totalHours = completedWorks
var totalUnits = completed
.SelectMany(cw => cw.Workers)
.Where(wcw => wcw.WorkerId == worker.Id)
.Sum(wcw => wcw.NumberOfWorkingHours);
var specialization = _specializationStorageContract.GetElementById(worker.SpecializationId)
var specialization = specializationStorageContract
.GetElementById(worker.SpecializationId)
?? throw new NullListException();
var salary = specialization.Salary * totalHours;
var cfg = worker.ConfigurationModel;
double salaryToPay;
_logger.LogDebug("Calculated salary for worker {WorkerId}: {Salary}", worker.Id, salary);
switch (cfg)
{
case OvertimeSalaryConfiguration otCfg:
salaryToPay = CalculateOvertimeParallel(otCfg, totalUnits);
break;
_salaryStorageContract.AddElement(new SalaryDataModel(Guid.NewGuid().ToString(), worker.Id, salary));
case PieceworkSalaryConfiguration pwCfg:
salaryToPay = CalculatePieceworkParallel(pwCfg, completed, worker.Id);
break;
default:
salaryToPay = cfg.Rate * totalUnits;
break;
}
logger.LogDebug("Worker {WorkerId} totalUnits={Units} salary={Salary}", worker.Id, totalUnits, salaryToPay);
salaryStorageContract.AddElement(
new SalaryDataModel(Guid.NewGuid().ToString(), worker.Id, salaryToPay)
);
}
}
public void InsertSalary(SalaryDataModel model)
private double CalculateOvertimeParallel(OvertimeSalaryConfiguration cfg, double hours)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
const double standardHours = 160;
logger.LogInformation("Inserting salary: {SalaryId}", model.Id);
var t1 = Task.Run(() => Math.Min(hours, standardHours) * cfg.Rate);
var t2 = Task.Run(() => Math.Max(0, hours - standardHours) * cfg.Rate * cfg.OvertimeMultiplier);
if (!Guid.TryParse(model.Id, out _))
throw new ValidationException("Salary ID is not a valid GUID.");
if (!Guid.TryParse(model.WorkerId, out _))
throw new ValidationException("Worker ID is not a valid GUID.");
var worker = workerStorageContract.GetElementById(model.WorkerId)
?? throw new ElementNotFoundException(model.WorkerId);
salaryStorageContract.AddElement(model);
logger.LogDebug("Salary inserted for worker {WorkerId}", model.WorkerId);
Task.WaitAll(t1, t2);
return t1.Result + t2.Result;
}
private double CalculatePieceworkParallel(
PieceworkSalaryConfiguration cfg,
List<ComplitedWorkDataModel> completed,
string workerId)
{
var units = completed
.SelectMany(cw => cw.Workers)
.Where(wcw => wcw.WorkerId == workerId)
.Select(wcw => wcw.NumberOfWorkingHours)
.ToList();
if (units.Count == 0)
return 0;
var maxThreads = configurationSalary.MaxParallelThreads;
var semaphore = new SemaphoreSlim(maxThreads);
var chunkSize = (int)Math.Ceiling(units.Count / (double)maxThreads);
var tasks = new List<Task<double>>();
for (int i = 0; i < units.Count; i += chunkSize)
{
var chunk = units.Skip(i).Take(chunkSize).ToList();
tasks.Add(Task.Run(() =>
{
semaphore.Wait();
try
{
return chunk.Sum(u => u * cfg.UnitRate);
}
finally
{
semaphore.Release();
}
}));
}
Task.WaitAll(tasks.ToArray());
semaphore.Dispose();
var basePay = tasks.Sum(t => t.Result);
var totalUnits = units.Sum();
var bonusUnits = Math.Max(0, totalUnits - configurationSalary.BonusThreshold);
var bonusPay = bonusUnits * cfg.BonusPerUnit;
return basePay + bonusPay;
}
}

View File

@@ -6,5 +6,4 @@ public interface ISalaryBusinessLogicContract
List<SalaryDataModel> GetSalariesByWorkerByPeriod(string workerId, DateTime fromDate, DateTime toDate);
void CalculateSalaryByMonth(DateTime date);
public void InsertSalary(SalaryDataModel model);
}

View File

@@ -0,0 +1,8 @@
namespace TwoFromTheCasketContracts.Infastructure;
public interface IConfigurationSalary
{
int MaxParallelThreads { get; }
double BonusThreshold { get; }
}

View File

@@ -6,7 +6,5 @@ public class PieceworkSalaryConfiguration : SalaryConfiguration
public double UnitRate { get; set; }
public double BonusThreshold { get; set; }
public double BonusPerUnit { get; set; }
}

View File

@@ -38,36 +38,38 @@ public class ComplitedWorkStorageContract : IComplitedWorkStorageContract
try
{
var query = _dbContext.ComplitedWorks
.Include(x => x.Work)
.Include(x => x.Room)
.Include(x => x.WorkersComplitedWorks)
.Include(cw => cw.WorkersComplitedWorks)
.Where(cw => cw.Date >= startTime && cw.Date <= endTime)
.AsQueryable();
if (startTime.HasValue)
query = query.Where(x => x.Date >= startTime.Value);
if (endTime.HasValue)
query = query.Where(x => x.Date <= endTime.Value);
if (!string.IsNullOrEmpty(RoomId))
query = query.Where(x => x.RoomId == RoomId);
if (!string.IsNullOrEmpty(WorkerId))
query = query.Where(cw =>
cw.WorkersComplitedWorks.Any(wcw => wcw.WorkerId == WorkerId));
if (!string.IsNullOrEmpty(WorkId))
query = query.Where(x => x.WorkId == WorkId);
query = query.Where(cw => cw.WorkId == WorkId);
if (!string.IsNullOrEmpty(WorkerId))
query = query.Where(x => x.WorkersComplitedWorks.Any(wcw => wcw.WorkerId == WorkerId));
if (!string.IsNullOrEmpty(RoomId))
query = query.Where(cw => cw.RoomId == RoomId);
var result = query
.Select(x => _mapper.Map<ComplitedWorkDataModel>(x))
return query
.Select(cw => new ComplitedWorkDataModel(
cw.Id,
cw.WorkId,
cw.RoomId,
cw.WorkersComplitedWorks
.Select(wcw => new WorkerComplitedWorkDataModel(
wcw.WorkerId,
wcw.ComplitedWorkId,
wcw.NumberOfWorkingHours))
.ToList()
))
.ToList();
return result ?? new List<ComplitedWorkDataModel>();
}
catch (Exception e)
catch
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(e);
throw;
}
}

View File

@@ -7,7 +7,11 @@ using TwoFromTheCasketBusinessLogic.Implementation;
using TwoFromTheCasketContracts.BusinessLogicsContracts;
using TwoFromTheCasketContracts.DataModels;
using TwoFromTheCasketContracts.Exceptions;
using TwoFromTheCasketContracts.Infastructure;
using TwoFromTheCasketContracts.Infastructure.SalaryConfiguration;
using TwoFromTheCasketContracts.StorageContracts;
using TwoFromTheCasketTests.Infrastructure;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace TwoFromTheCasketTests.BusinessLogicsContractsTests;
@@ -20,6 +24,7 @@ public class SalaryBusinessLogicContractTests
private Mock<IWorkerStorageContract> _workerStorageMock;
private Mock<IComplitedWorkStorageContract> _complitedWorkStorageMock;
private Mock<ISpecializationStorageContract> _specializationStorageMock;
private readonly ConfigurationSalaryTest _configurationSalary = new();
[OneTimeSetUp]
public void OneTimeSetUp()
@@ -29,16 +34,18 @@ public class SalaryBusinessLogicContractTests
_complitedWorkStorageMock = new Mock<IComplitedWorkStorageContract>();
_specializationStorageMock = new Mock<ISpecializationStorageContract>();
_salaryBusinessLogicContract = new SalaryBusinessLogicContract(
_salaryStorageMock.Object,
_workerStorageMock.Object,
_complitedWorkStorageMock.Object,
_specializationStorageMock.Object,
new Mock<ILogger>().Object);
new Mock<ILogger>().Object,
_configurationSalary);
}
[SetUp]
public void SetUp()
[TearDown]
public void TearDown()
{
_salaryStorageMock.Reset();
_workerStorageMock.Reset();
@@ -49,276 +56,416 @@ public class SalaryBusinessLogicContractTests
[Test]
public void GetSalariesByWorkerByPeriod_ReturnListOfRecords_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
//var workerId = Guid.NewGuid().ToString();
//var listOriginal = new List<SalaryDataModel>
//{
// new(Guid.NewGuid().ToString(), workerId, 5000),
// new(Guid.NewGuid().ToString(), workerId, 6000),
// new(Guid.NewGuid().ToString(), workerId, 7000)
//};
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
var workerId = Guid.NewGuid().ToString();
var listOriginal = new List<SalaryDataModel>
{
new(Guid.NewGuid().ToString(), workerId, 5000),
new(Guid.NewGuid().ToString(), workerId, 6000),
new(Guid.NewGuid().ToString(), workerId, 7000)
};
//_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, workerId)).Returns(listOriginal);
_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, workerId)).Returns(listOriginal);
//var list = _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate);
var list = _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate);
//Assert.Multiple(() =>
//{
// Assert.That(list, Is.Not.Null);
// Assert.That(list, Is.EquivalentTo(listOriginal));
//});
Assert.Multiple(() =>
{
Assert.That(list, Is.Not.Null);
Assert.That(list, Is.EquivalentTo(listOriginal));
});
//_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, workerId), Times.Once);
_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, workerId), Times.Once);
}
[Test]
public void GetSalariesByWorkerByPeriod_ReturnEmptyList_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
//var workerId = Guid.NewGuid().ToString();
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
var workerId = Guid.NewGuid().ToString();
//_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, workerId)).Returns(new List<SalaryDataModel>());
_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, workerId)).Returns(new List<SalaryDataModel>());
//var list = _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate);
var list = _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate);
//Assert.Multiple(() =>
//{
// Assert.That(list, Is.Not.Null);
// Assert.That(list, Has.Count.EqualTo(0));
//});
Assert.Multiple(() =>
{
Assert.That(list, Is.Not.Null);
Assert.That(list, Has.Count.EqualTo(0));
});
//_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, workerId), Times.Once);
_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, workerId), Times.Once);
}
[Test]
public void GetSalariesByWorkerByPeriod_IncorrectDates_ThrowException_Test()
{
//var date = DateTime.UtcNow;
//var workerId = Guid.NewGuid().ToString();
var date = DateTime.UtcNow;
var workerId = Guid.NewGuid().ToString();
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, date, date), Throws.TypeOf<IncorrectDatesException>());
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, date, date.AddSeconds(-1)), Throws.TypeOf<IncorrectDatesException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, date, date), Throws.TypeOf<IncorrectDatesException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, date, date.AddSeconds(-1)), Throws.TypeOf<IncorrectDatesException>());
//_salaryStorageMock.Verify(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>()), Times.Never);
_salaryStorageMock.Verify(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>()), Times.Never);
}
[Test]
public void GetSalariesByWorkerByPeriod_WorkerIdIsNullOrEmpty_ThrowException_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(null, fromDate, toDate), Throws.TypeOf<ArgumentNullException>());
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(string.Empty, fromDate, toDate), Throws.TypeOf<ArgumentNullException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(null, fromDate, toDate), Throws.TypeOf<ArgumentNullException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(string.Empty, fromDate, toDate), Throws.TypeOf<ArgumentNullException>());
//_salaryStorageMock.Verify(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>()), Times.Never);
_salaryStorageMock.Verify(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>()), Times.Never);
}
[Test]
public void GetSalariesByWorkerByPeriod_WorkerIdIsNotGuid_ThrowException_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
//var workerId = "invalid-guid";
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
var workerId = "invalid-guid";
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate), Throws.TypeOf<ValidationException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate), Throws.TypeOf<ValidationException>());
//_salaryStorageMock.Verify(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>()), Times.Never);
_salaryStorageMock.Verify(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>()), Times.Never);
}
[Test]
public void GetSalariesByWorkerByPeriod_ReturnNull_ThrowException_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
//var workerId = Guid.NewGuid().ToString();
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
var workerId = Guid.NewGuid().ToString();
//_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, workerId)).Returns((List<SalaryDataModel>)null);
_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, workerId)).Returns((List<SalaryDataModel>)null);
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate), Throws.TypeOf<NullListException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate), Throws.TypeOf<NullListException>());
//_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, workerId), Times.Once);
_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, workerId), Times.Once);
}
[Test]
public void GetSalariesByWorkerByPeriod_StorageThrowError_ThrowException_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
//var workerId = Guid.NewGuid().ToString();
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
var workerId = Guid.NewGuid().ToString();
//_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, workerId)).Throws(new StorageException(new InvalidOperationException()));
_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, workerId)).Throws(new StorageException(new InvalidOperationException()));
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate), Throws.TypeOf<StorageException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByWorkerByPeriod(workerId, fromDate, toDate), Throws.TypeOf<StorageException>());
//_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, workerId), Times.Once);
_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, workerId), Times.Once);
}
[Test]
public void GetSalariesByPeriod_ReturnListOfRecords_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
//var listOriginal = new List<SalaryDataModel>
//{
// new(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5000),
// new(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 6000),
// new(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 7000)
//};
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
var listOriginal = new List<SalaryDataModel>
{
new(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 5000),
new(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 6000),
new(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 7000)
};
//_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, null)).Returns(listOriginal);
_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, null)).Returns(listOriginal);
//var list = _salaryBusinessLogicContract.GetSalariesByPeriod(fromDate, toDate);
var list = _salaryBusinessLogicContract.GetSalariesByPeriod(fromDate, toDate);
//Assert.Multiple(() =>
//{
// Assert.That(list, Is.Not.Null);
// Assert.That(list, Is.EquivalentTo(listOriginal));
//});
Assert.Multiple(() =>
{
Assert.That(list, Is.Not.Null);
Assert.That(list, Is.EquivalentTo(listOriginal));
});
//_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, null), Times.Once);
_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, null), Times.Once);
}
[Test]
public void GetSalariesByPeriod_ReturnEmptyList_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
//_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, null)).Returns(new List<SalaryDataModel>());
_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, null)).Returns(new List<SalaryDataModel>());
//var list = _salaryBusinessLogicContract.GetSalariesByPeriod(fromDate, toDate);
var list = _salaryBusinessLogicContract.GetSalariesByPeriod(fromDate, toDate);
//Assert.Multiple(() =>
//{
// Assert.That(list, Is.Not.Null);
// Assert.That(list, Has.Count.EqualTo(0));
//});
Assert.Multiple(() =>
{
Assert.That(list, Is.Not.Null);
Assert.That(list, Has.Count.EqualTo(0));
});
//_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, null), Times.Once);
_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, null), Times.Once);
}
[Test]
public void GetSalariesByPeriod_IncorrectDates_ThrowException_Test()
{
//var date = DateTime.UtcNow;
var date = DateTime.UtcNow;
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByPeriod(date, date), Throws.TypeOf<IncorrectDatesException>());
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByPeriod(date, date.AddSeconds(-1)), Throws.TypeOf<IncorrectDatesException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByPeriod(date, date), Throws.TypeOf<IncorrectDatesException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByPeriod(date, date.AddSeconds(-1)), Throws.TypeOf<IncorrectDatesException>());
//_salaryStorageMock.Verify(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>()), Times.Never);
_salaryStorageMock.Verify(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>()), Times.Never);
}
[Test]
public void GetSalariesByPeriod_ReturnNull_ThrowException_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
//_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, null)).Returns((List<SalaryDataModel>)null);
_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, null)).Returns((List<SalaryDataModel>)null);
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByPeriod(fromDate, toDate), Throws.TypeOf<NullListException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByPeriod(fromDate, toDate), Throws.TypeOf<NullListException>());
//_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, null), Times.Once);
_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, null), Times.Once);
}
[Test]
public void GetSalariesByPeriod_StorageThrowError_ThrowException_Test()
{
//var fromDate = DateTime.UtcNow.AddMonths(-1);
//var toDate = DateTime.UtcNow;
var fromDate = DateTime.UtcNow.AddMonths(-1);
var toDate = DateTime.UtcNow;
//_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, null)).Throws(new StorageException(new InvalidOperationException()));
_salaryStorageMock.Setup(x => x.GetList(fromDate, toDate, null)).Throws(new StorageException(new InvalidOperationException()));
//Assert.That(() => _salaryBusinessLogicContract.GetSalariesByPeriod(fromDate, toDate), Throws.TypeOf<StorageException>());
Assert.That(() => _salaryBusinessLogicContract.GetSalariesByPeriod(fromDate, toDate), Throws.TypeOf<StorageException>());
//_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, null), Times.Once);
_salaryStorageMock.Verify(x => x.GetList(fromDate, toDate, null), Times.Once);
}
[Test]
public void CalculateSalaryByMounth_CalculateSalary_Test()
public void CalculateSalaryByMonth_CalculateSalary_Test()
{
//var workerId = Guid.NewGuid().ToString();
//var workId = Guid.NewGuid().ToString();
//var roomId = Guid.NewGuid().ToString();
//var numberOfWorkingHours = 10.0;
//var specializationSalary = 2000.0;
//var expectedSalary = (specializationSalary * numberOfWorkingHours);
var date = DateTime.UtcNow;
var workerId = Guid.NewGuid().ToString();
var workId = Guid.NewGuid().ToString();
var roomId = Guid.NewGuid().ToString();
var hours = 10.0;
var specSalary = 2000.0;
var expected = specSalary * hours;
//_complitedWorkStorageMock.Setup(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string?>(), It.IsAny<string?>(), It.IsAny<string?>()))
// .Returns([
// new ComplitedWorkDataModel(
// Guid.NewGuid().ToString(),
// workId,
// roomId,
// [new WorkerComplitedWorkDataModel(workerId, workId, numberOfWorkingHours)])
// ]);
var wm = new WorkerDataModel(
workerId,
"Test Worker",
Guid.NewGuid().ToString(),
"+7-777-777-77-77",
DateTime.UtcNow.AddYears(-30),
new SalaryConfiguration { Rate = specSalary }
);
_workerStorageMock
.Setup(x => x.GetList(
It.IsAny<bool>(),
It.IsAny<string?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>()))
.Returns(new List<WorkerDataModel> { wm });
//_specializationStorageMock.Setup(x => x.GetElementById(It.IsAny<string>()))
// .Returns(new SpecializationDataModel(Guid.NewGuid().ToString(), "Specialist", specializationSalary, true, DateTime.UtcNow));
var cw = new ComplitedWorkDataModel(
Guid.NewGuid().ToString(),
workId,
roomId,
new List<WorkerComplitedWorkDataModel>
{
new WorkerComplitedWorkDataModel(workerId, workId, hours)
}
);
_complitedWorkStorageMock
.Setup(x => x.GetList(
It.IsAny<DateTime>(),
It.IsAny<DateTime>(),
It.IsAny<string?>(),
It.IsAny<string?>(),
It.IsAny<string?>()))
.Returns(new List<ComplitedWorkDataModel> { cw });
//_workerStorageMock.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
// .Returns([new WorkerDataModel(workerId, "Test Worker", Guid.NewGuid().ToString(), "+7-777-777-77-77", DateTime.UtcNow.AddYears(-30))]);
_specializationStorageMock
.Setup(x => x.GetElementById(wm.SpecializationId))
.Returns(new SpecializationDataModel(
Guid.NewGuid().ToString(),
"Specialist",
specSalary,
true,
DateTime.UtcNow));
//var calculatedSalary = 0.0;
double recorded = 0;
_salaryStorageMock
.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
.Callback<SalaryDataModel>(s => recorded = s.Sum);
//_salaryStorageMock.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
// .Callback((SalaryDataModel x) => { calculatedSalary = x.Sum; });
_salaryBusinessLogicContract.CalculateSalaryByMonth(date);
//_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
//Assert.That(calculatedSalary, Is.EqualTo(expectedSalary));
}
[Test]
public void CalculateSalaryByMounth_WithSeveralWorkers_Test()
{
//var workerIds = new List<string> { Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString() };
//var numberOfWorkingHours = new List<double> { 5.0, 8.0, 12.0 };
//var specializationSalary = 2000.0;
//var expectedSalaries = workerIds.Select((id, index) => (specializationSalary * numberOfWorkingHours[index])).ToList();
//var workers = workerIds.Select((id, index) => new WorkerDataModel(id, $"Worker {index + 1}", Guid.NewGuid().ToString(), "+7-777-777-77-77", DateTime.UtcNow.AddYears(-30))).ToList();
//_complitedWorkStorageMock.Setup(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
// .Returns(workerIds.Select((id, index) =>
// new ComplitedWorkDataModel(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(),
// [new WorkerComplitedWorkDataModel(id, Guid.NewGuid().ToString(), numberOfWorkingHours[index])])
// ).ToList());
//_specializationStorageMock.Setup(x => x.GetElementById(It.IsAny<string>()))
// .Returns(new SpecializationDataModel(Guid.NewGuid().ToString(), "Specialist", specializationSalary, true, DateTime.UtcNow));
//_workerStorageMock.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
// .Returns(workers);
//var calculatedSalaries = new List<double>();
//_salaryStorageMock.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
// .Callback((SalaryDataModel x) => { calculatedSalaries.Add(x.Sum); });
//_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
//Assert.That(calculatedSalaries, Is.EquivalentTo(expectedSalaries));
Assert.That(recorded, Is.EqualTo(expected));
}
[Test]
public void CalculateSalaryByMounth_WorkerStorageReturnNull_ThrowException_Test()
{
//_workerStorageMock.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
// .Throws(new StorageException(new InvalidOperationException()));
_workerStorageMock.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Throws(new StorageException(new InvalidOperationException()));
//Assert.That(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow), Throws.TypeOf<StorageException>());
Assert.That(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow), Throws.TypeOf<StorageException>());
}
[Test]
public void CalculateSalaryByMounth_ComplitedWorkStorageReturnNull_ThrowException_Test()
{
//_complitedWorkStorageMock.Setup(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
// .Throws(new StorageException(new InvalidOperationException()));
_complitedWorkStorageMock.Setup(x => x.GetList(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Throws(new StorageException(new InvalidOperationException()));
//Assert.That(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow), Throws.TypeOf<StorageException>());
Assert.That(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow), Throws.TypeOf<StorageException>());
}
[Test]
public void CalculateSalaryByMonth_OvertimeParallel_StandardAndOvertime_Test()
{
var workerId = Guid.NewGuid().ToString();
var hoursWorked = 200.0;
var rate = 100.0;
var multiplier = 2.0;
var overtimeCfg = new OvertimeSalaryConfiguration
{
Rate = rate,
OvertimeMultiplier = multiplier
};
_workerStorageMock
.Setup(x => x.GetList(
It.IsAny<bool>(),
It.IsAny<string?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>()))
.Returns(new List<WorkerDataModel>
{
new WorkerDataModel(
workerId,
"Overworker",
Guid.NewGuid().ToString(),
"+70000000002",
DateTime.UtcNow.AddYears(-30),
overtimeCfg
)
});
var cw = new ComplitedWorkDataModel(
Guid.NewGuid().ToString(),
"WK",
"RM",
new List<WorkerComplitedWorkDataModel>
{
new WorkerComplitedWorkDataModel(workerId, "WK", hoursWorked)
}
);
_complitedWorkStorageMock
.Setup(x => x.GetList(
It.IsAny<DateTime>(),
It.IsAny<DateTime>(),
workerId,
It.IsAny<string?>(),
It.IsAny<string?>()))
.Returns(new List<ComplitedWorkDataModel> { cw });
_specializationStorageMock
.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new SpecializationDataModel(
Guid.NewGuid().ToString(),
"Spec",
0,
true,
DateTime.UtcNow));
double recorded = 0;
_salaryStorageMock
.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
.Callback<SalaryDataModel>(s => recorded = s.Sum);
_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
var expected = 160 * rate + (hoursWorked - 160) * rate * multiplier;
Assert.That(recorded, Is.EqualTo(expected));
}
[Test]
public void CalculateSalaryByMonth_PieceworkParallel_ChunkingAndBonus_Test()
{
var workerId = Guid.NewGuid().ToString();
var units = new List<double> { 5, 7, 4 };
var unitRate = 100.0;
var bonusPerUnit = 10.0;
var pieceCfg = new PieceworkSalaryConfiguration
{
Rate = unitRate,
UnitRate = unitRate,
BonusPerUnit = bonusPerUnit
};
_workerStorageMock
.Setup(x => x.GetList(
It.IsAny<bool>(),
It.IsAny<string?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(),
It.IsAny<DateTime?>()))
.Returns(new List<WorkerDataModel>
{
new WorkerDataModel(
workerId,
"Pieceworker",
Guid.NewGuid().ToString(),
"+70000000003",
DateTime.UtcNow.AddYears(-30),
pieceCfg
)
});
var cw = new ComplitedWorkDataModel(
Guid.NewGuid().ToString(),
"W1",
"R1",
units.Select(u => new WorkerComplitedWorkDataModel(workerId, "W1", u)).ToList()
);
_complitedWorkStorageMock
.Setup(x => x.GetList(
It.IsAny<DateTime>(),
It.IsAny<DateTime>(),
workerId,
It.IsAny<string?>(),
It.IsAny<string?>()))
.Returns(new List<ComplitedWorkDataModel> { cw });
_specializationStorageMock
.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new SpecializationDataModel(
Guid.NewGuid().ToString(),
"Spec",
0,
true,
DateTime.UtcNow));
double recorded = 0;
_salaryStorageMock
.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
.Callback<SalaryDataModel>(s => recorded = s.Sum);
_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
Assert.That(recorded, Is.EqualTo(1600 + 60));
}
}

View File

@@ -0,0 +1,9 @@
using TwoFromTheCasketContracts.Infastructure;
namespace TwoFromTheCasketTests.Infrastructure;
class ConfigurationSalaryTest : IConfigurationSalary
{
public int MaxParallelThreads => 4;
public double BonusThreshold => 10;
}

View File

@@ -16,9 +16,10 @@ public static class TwoFromTheCasketDbContextExtensions
SalaryConfiguration? configuration = null,
string? phone = null,
DateTime? birthday = null,
DateTime? validFrom = null)
DateTime? validFrom = null,
SalaryConfiguration? _configuration = null)
{
var config = configuration ?? new SalaryConfiguration { Rate = 0 };
var config = _configuration ?? new SalaryConfiguration { Rate = 1 };
var worker = new Worker
{
@@ -125,15 +126,21 @@ public static class TwoFromTheCasketDbContextExtensions
return work;
}
public static ComplitedWork InsertComplitedWorkToDatabaseAndReturn(this TwoFromTheCasketDbContext db,
string? id = null, string roomId = "room1", string workId = "work1", DateTime? date = null, WorkerComplitedWork? workerComplitedWork = null)
public static ComplitedWork InsertComplitedWorkToDatabaseAndReturn(
this TwoFromTheCasketDbContext db,
string? id = null,
string roomId = "room1",
string workId = "work1",
DateTime? date = null,
WorkerComplitedWork? workerComplitedWork = null)
{
var complited = new ComplitedWork
{
Id = id ?? Guid.NewGuid().ToString(),
RoomId = roomId,
WorkId = workId,
Date = date ?? DateTime.UtcNow
Date = date ?? DateTime.UtcNow,
WorkersComplitedWorks = new List<WorkerComplitedWork>()
};
if (workerComplitedWork != null)

View File

@@ -221,7 +221,7 @@ namespace TwoFromTheCasketTests.StorageContracts
public void Try_AddElement_WithPieceworkSalaryConfiguration_Test()
{
var spec = InsertSpecializationToDatabase(Guid.NewGuid().ToString());
var conf = new PieceworkSalaryConfiguration { Rate = 50, UnitRate = 5, BonusThreshold = 100, BonusPerUnit = 2 };
var conf = new PieceworkSalaryConfiguration { Rate = 50, UnitRate = 5, BonusPerUnit = 2 };
var worker = CreateWorkerDataModel(Guid.NewGuid().ToString(), conf);
_workerStorageContract.AddElement(worker);
@@ -233,7 +233,6 @@ namespace TwoFromTheCasketTests.StorageContracts
Assert.Multiple(() =>
{
Assert.That(pw.UnitRate, Is.EqualTo(5));
Assert.That(pw.BonusThreshold, Is.EqualTo(100));
Assert.That(pw.BonusPerUnit, Is.EqualTo(2));
});
}

View File

@@ -1,6 +1,7 @@
using System.Net;
using System.Text.Json;
using TwoFromTheCasketContracts.BindingModels;
using TwoFromTheCasketContracts.Infastructure.SalaryConfiguration;
using TwoFromTheCasketContracts.ViewModels;
using TwoFromTheCasketDatabase.Models;
using TwoFromTheCasketTests.Infrastructure;
@@ -36,8 +37,10 @@ public class SalaryControllerTest : BaseWebApiControllerTest
var roomId = Guid.NewGuid().ToString();
var complitedId = Guid.NewGuid().ToString();
var work = TwoFromTheCasketDb.InsertWorkToDatabaseAndReturn(id: workId, date: dateValid);
var room = TwoFromTheCasketDb.InsertRoomToDatabaseAndReturn(id: roomId);
var work = TwoFromTheCasketDb
.InsertWorkToDatabaseAndReturn(id: workId, date: dateValid);
var room = TwoFromTheCasketDb
.InsertRoomToDatabaseAndReturn(id: roomId);
var complited = TwoFromTheCasketDb.InsertComplitedWorkToDatabaseAndReturn(
id: complitedId,
@@ -57,11 +60,79 @@ public class SalaryControllerTest : BaseWebApiControllerTest
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var salary = TwoFromTheCasketDb.Salaries.FirstOrDefault(x => x.WorkerId == _worker.Id);
var salary = TwoFromTheCasketDb
.Salaries
.FirstOrDefault(x => x.WorkerId == _worker.Id);
Assert.That(salary, Is.Not.Null);
Assert.That(salary.Sum, Is.GreaterThan(0));
}
[Test]
public async Task Try_Calculate_WithOvertimeConfiguration_Test()
{
var date = DateTime.UtcNow;
var overtime = new OvertimeSalaryConfiguration
{
Rate = 100,
OvertimeMultiplier = 1.75
};
var w = TwoFromTheCasketDb.Workers.First(x => x.Id == _worker.Id);
w.Configuration = overtime;
TwoFromTheCasketDb.SaveChanges();
var work = TwoFromTheCasketDb.InsertWorkToDatabaseAndReturn(date: date);
var room = TwoFromTheCasketDb.InsertRoomToDatabaseAndReturn();
var compl = TwoFromTheCasketDb.InsertComplitedWorkToDatabaseAndReturn(
date: date, roomId: room.Id, workId: work.Id);
TwoFromTheCasketDb.InsertWorkerComplitedWorkToDatabaseAndReturn(
complitedWorkId: compl.Id,
workerId: _worker.Id,
numberOfWorkingHour: 10
);
var resp = await HttpClient.GetAsync($"/api/Salary/Calculate/{date:O}");
Assert.That(resp.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var sal = TwoFromTheCasketDb.Salaries.First(x => x.WorkerId == _worker.Id);
Assert.That(sal.Sum, Is.EqualTo(10 * 100 * 1.75));
}
[Test]
public async Task Try_Calculate_WithPieceworkConfiguration_Test()
{
var date = DateTime.UtcNow;
var piece = new PieceworkSalaryConfiguration
{
Rate = 1,
UnitRate = 50,
BonusPerUnit = 10
};
var w = TwoFromTheCasketDb.Workers.First(x => x.Id == _worker.Id);
w.Configuration = piece;
TwoFromTheCasketDb.SaveChanges();
var work = TwoFromTheCasketDb.InsertWorkToDatabaseAndReturn(date: date);
var room = TwoFromTheCasketDb.InsertRoomToDatabaseAndReturn();
var compl = TwoFromTheCasketDb.InsertComplitedWorkToDatabaseAndReturn(
date: date, roomId: room.Id, workId: work.Id);
TwoFromTheCasketDb.InsertWorkerComplitedWorkToDatabaseAndReturn(
complitedWorkId: compl.Id,
workerId: _worker.Id,
numberOfWorkingHour: 8
);
var resp = await HttpClient.GetAsync($"/api/Salary/Calculate/{date:O}");
Assert.That(resp.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var sal = TwoFromTheCasketDb.Salaries.First(x => x.WorkerId == _worker.Id);
Assert.That(sal.Sum, Is.EqualTo(430));
}
[Test]
public async Task Try_Calculate_WhenNoCompletedWork_Test()
{

View File

@@ -278,7 +278,6 @@ public class WorkerControllerTest : BaseWebApiControllerTest
{
Rate = 80,
UnitRate = 7,
BonusThreshold = 50,
BonusPerUnit = 1.5
};
var json = JsonConvert.SerializeObject(piecework);
@@ -311,7 +310,6 @@ public class WorkerControllerTest : BaseWebApiControllerTest
Assert.Multiple(() =>
{
Assert.That(pw.UnitRate, Is.EqualTo(7));
Assert.That(pw.BonusThreshold, Is.EqualTo(50));
Assert.That(pw.BonusPerUnit, Is.EqualTo(1.5));
});
}

View File

@@ -0,0 +1,16 @@
using System.Configuration;
using TwoFromTheCasketContracts.Infastructure;
namespace TwoFromTheCasketWebApi.Infrastructure;
public class ConfigurationSalary(IConfiguration configuration) : IConfigurationSalary
{
private readonly Lazy<SalarySettings> _SalarySettings = new(() =>
{
return configuration.GetValue<SalarySettings>("SalarySettings") ?? throw new InvalidDataException(nameof(SalarySettings));
});
public int MaxParallelThreads => _SalarySettings.Value.MaxParallelThreads;
public double BonusThreshold => _SalarySettings.Value.BonusThreshold;
}

View File

@@ -0,0 +1,6 @@
namespace TwoFromTheCasketWebApi.Infrastructure;
public class SalarySettings
{
public int MaxParallelThreads { get; set; }
public double BonusThreshold { get; set; }
}

View File

@@ -55,6 +55,7 @@ public class Program
builder.Services.AddOpenApi();
builder.Services.AddSingleton<IConfigurationDatabase, ConfigurationDatabase>();
builder.Services.AddSingleton<IConfigurationSalary, ConfigurationSalary>();
builder.Services.AddTransient<TwoFromTheCasketDbContext>();

View File

@@ -24,5 +24,9 @@
}
]
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"SalarySettings": {
"MaxParallelThreads": 4,
"BonusThreshold": 10
}
}