food-market/tests/food-market.UnitTests/Support/SqliteDb.cs
nns f3d517f257 test(unit): xUnit-проект food-market.UnitTests, 23 теста (P1-20)
Чистая логика вынесена в 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>
2026-05-27 03:01:56 +05:00

39 lines
1.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}