using FluentAssertions;
using foodmarket.Api.Background;
using foodmarket.Domain.Inventory;
using foodmarket.Domain.Platform;
using foodmarket.Infrastructure.Persistence;
using foodmarket.UnitTests.Support;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace foodmarket.UnitTests;
/// Юнит-тест: HousekeepingJobs.PruneStockMovements удаляет только
/// движения старше N дней (по умолчанию 730). Stock-инвариант не проверяем —
/// это межтенантная задача очистки далёкой истории, документы за этот горизонт
/// в норме закрыты.
public class HousekeepingJobsTests
{
private static IConfiguration CfgWith(int stockDays = 730, int auditDays = 90) =>
new ConfigurationBuilder().AddInMemoryCollection(new Dictionary
{
["Hangfire:Retention:StockMovementDays"] = stockDays.ToString(),
["Hangfire:Retention:AuditLogDays"] = auditDays.ToString(),
}).Build();
[Fact]
public async Task PruneStockMovements_deletes_only_old_rows()
{
using var sqlite = new SqliteDb(foreignKeys: false);
var orgId = Guid.NewGuid();
var tenant = new FakeTenantContext { OrganizationId = orgId };
using (var db = sqlite.Create(tenant))
{
db.StockMovements.AddRange(
new StockMovement
{
OrganizationId = orgId, ProductId = Guid.NewGuid(), StoreId = Guid.NewGuid(),
Quantity = 1m, Type = MovementType.Supply, DocumentType = "supply",
OccurredAt = DateTime.UtcNow.AddYears(-3), // старая
},
new StockMovement
{
OrganizationId = orgId, ProductId = Guid.NewGuid(), StoreId = Guid.NewGuid(),
Quantity = 1m, Type = MovementType.Supply, DocumentType = "supply",
OccurredAt = DateTime.UtcNow.AddDays(-30), // свежая
});
await db.SaveChangesAsync();
}
int deleted;
using (var db = sqlite.Create(tenant))
{
var jobs = new HousekeepingJobs(db, CfgWith(stockDays: 730), NullLogger.Instance);
deleted = await jobs.PruneStockMovementsAsync();
}
deleted.Should().Be(1);
using (var db = sqlite.Create(tenant))
{
(await db.StockMovements.IgnoreQueryFilters().CountAsync()).Should().Be(1);
}
}
}