Чистая логика вынесена в Application для тестируемости и используется контроллерами: - MovingAverageCost.Compute (скользящее среднее себестоимости) ← SuppliesController.Post - RetailPaymentValidator.IsSufficient (достаточность оплаты) ← RetailSalesController.Post Тесты: - MovingAverageCost: первая приёмка, средневзвешенное, округление до 4 знаков, totalQty=0. - RetailPaymentValidator: ровно/переплата/недоплата, округление до 2 знаков. - StockService.ApplyMovement (SQLite in-memory): материализация Stock+движение, инкремент, отрицательное списание, throw без tenant. - Мультитенантный query-filter AppDbContext: tenant видит своё; чужой не видит; SuperAdmin без override — всё; с override — только выбранную оргу. Все 23 зелёные. EF8 SQLite поддерживает ToJson (EmployeeRole.Permissions). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
39 lines
1.9 KiB
C#
39 lines
1.9 KiB
C#
using foodmarket.Application.Common.Tenancy;
|
||
using foodmarket.Infrastructure.Persistence;
|
||
using Microsoft.Data.Sqlite;
|
||
using Microsoft.EntityFrameworkCore;
|
||
|
||
namespace foodmarket.UnitTests.Support;
|
||
|
||
/// <summary>SQLite in-memory БД для тестов, использующих реальный AppDbContext
|
||
/// (query-фильтр мультитенантности, StockService). Соединение держим открытым —
|
||
/// in-memory БД живёт, пока открыт коннект; разные DbContext'ы на одном коннекте
|
||
/// видят одни данные. EnsureCreated строит схему по реальной модели (включая
|
||
/// tenant query-filter).</summary>
|
||
public sealed class SqliteDb : IDisposable
|
||
{
|
||
private readonly SqliteConnection _connection;
|
||
|
||
/// <param name="foreignKeys">false — отключить проверку FK (для фокусных
|
||
/// тестов логики, где не хотим засевать все родительские строки).</param>
|
||
public SqliteDb(bool foreignKeys = true)
|
||
{
|
||
_connection = new SqliteConnection($"DataSource=:memory:;Foreign Keys={foreignKeys}");
|
||
_connection.Open();
|
||
// Схему создаём контекстом-сидером с правами SuperAdmin (фильтр на запись
|
||
// не влияет, но единообразно). EnsureCreated идемпотентен в рамках коннекта.
|
||
using var db = Create(new FakeTenantContext { IsSuperAdmin = true });
|
||
db.Database.EnsureCreated();
|
||
}
|
||
|
||
public AppDbContext Create(ITenantContext tenant)
|
||
{
|
||
var options = new DbContextOptionsBuilder<AppDbContext>()
|
||
.UseSqlite(_connection)
|
||
.Options;
|
||
return new AppDbContext(options, tenant);
|
||
}
|
||
|
||
public void Dispose() => _connection.Dispose();
|
||
}
|