Сгруппировать тестовые проекты по областям

Deal.Tests.Unit, Deal.Telegram.Tests, Deal.Ai.Tests, Deal.Ml.Tests
разложены по областям (Modules/<X>, Api, Infrastructure, Contracts,
Grpc, ...), общие хелперы -> Support; namespace = папка, using
между областями добавлены итеративно по ошибкам сборки.
This commit is contained in:
Rustam Khalimov
2026-09-11 13:28:26 +03:00
parent e3a2692507
commit 5bfa92a4ac
212 changed files with 1982 additions and 1686 deletions
@@ -0,0 +1,69 @@
using Deal.Api;
using Deal.Api.Dtos;
using Deal.Api.Services;
using Deal.Tests.Unit.Support;
namespace Deal.Tests.Unit.Api;
/// <summary>
/// Тесты PipelinePumpGate — общий воркер-гейт pump тенанта (план Task 11, Ruling 8; аналог
/// asyncio.Lock pipeline.py L3842): admin/tick и фоновый цикл не разбирают очередь одного тенанта
/// одновременно, разные тенанты не связаны.
/// </summary>
/// <remarks>
/// Семантика — как прототип pump_once L901902: занятый pump тенанта → повторный вход (TryEnter) даёт
/// false, вызывающий пропускает проход (очередь дождётся следующего срабатывания). Exit освобождает
/// гейт; гейт потокобезопасен (ConcurrentDictionary + атомарный TryAdd), параллельные входы одного
/// тенанта выигрывает ровно один.
/// </remarks>
public sealed class PipelinePumpGateTests
{
// Тенант A теста.
private static readonly Guid TenantA = Guid.NewGuid();
// Тенант B теста.
private static readonly Guid TenantB = Guid.NewGuid();
[Fact]
public void TryEnter_FirstEnterWinsSecondForSameTenantIsRejected()
{
var gate = new PipelinePumpGate();
Assert.True(gate.TryEnter(TenantA)); // первый воркер (admin/tick или фоновый цикл) вошёл
Assert.False(gate.TryEnter(TenantA)); // второй для того же тенанта пропускает проход (L901–902)
}
[Fact]
public void Exit_ReleasesGateForSameTenant()
{
var gate = new PipelinePumpGate();
Assert.True(gate.TryEnter(TenantA));
gate.Exit(TenantA);
Assert.True(gate.TryEnter(TenantA)); // после finally-освобождения гейт снова свободен
}
[Fact]
public void TryEnter_DifferentTenantsEnterIndependently()
{
var gate = new PipelinePumpGate();
Assert.True(gate.TryEnter(TenantA));
Assert.True(gate.TryEnter(TenantB)); // pump разных тенантов не конкурирует (свои схемы/очереди)
gate.Exit(TenantA);
Assert.True(gate.TryEnter(TenantA)); // освобождение A не влияет на занятый B
Assert.False(gate.TryEnter(TenantB));
}
[Fact]
public void Exit_WithoutEnterDoesNotBreakLaterEntry()
{
var gate = new PipelinePumpGate();
// Exit без парного TryEnter не роняет состояние (нет «висящего» флага после сбоя до входа).
gate.Exit(TenantA);
Assert.True(gate.TryEnter(TenantA));
}
}