Deal.Tests.Unit, Deal.Telegram.Tests, Deal.Ai.Tests, Deal.Ml.Tests разложены по областям (Modules/<X>, Api, Infrastructure, Contracts, Grpc, ...), общие хелперы -> Support; namespace = папка, using между областями добавлены итеративно по ошибкам сборки.
70 lines
2.9 KiB
C#
70 lines
2.9 KiB
C#
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 L38–42): admin/tick и фоновый цикл не разбирают очередь одного тенанта
|
||
/// одновременно, разные тенанты не связаны.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// Семантика — как прототип pump_once L901–902: занятый 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));
|
||
}
|
||
}
|