diff --git a/src/core/tests/Deal.Tests.Unit/Api/IngressRateLimitInterceptorTests.cs b/src/core/tests/Deal.Tests.Unit/Api/IngressRateLimitInterceptorTests.cs index f234ab4..da58789 100644 --- a/src/core/tests/Deal.Tests.Unit/Api/IngressRateLimitInterceptorTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Api/IngressRateLimitInterceptorTests.cs @@ -45,7 +45,7 @@ public sealed class IngressRateLimitInterceptorTests [Fact] public async Task PushSource_ExceedingTenantWindow_ThirdRejectedOtherTenantPasses() { - var registry = new FakeTenantRegistry(Tenant(TenantA), Tenant(TenantB)); + var registry = TestRegistry.With(Tenant(TenantA), Tenant(TenantB)); await RunAsync( registry, @@ -73,7 +73,7 @@ public sealed class IngressRateLimitInterceptorTests [Fact] public async Task HealthCheck_IsNotRateLimited_WhenIngressWindowExhausted() { - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync( registry, @@ -110,7 +110,7 @@ public sealed class IngressRateLimitInterceptorTests // scenario: Сценарий с gRPC-каналом. // permitsPerMinute: Окно сценария (GrpcIngressPerMinute опций хоста). private static Task RunAsync( - FakeTenantRegistry registry, + ITenantRepository registry, Action registerTenantServices, Func scenario, int permitsPerMinute = DefaultPermitsPerMinute) diff --git a/src/core/tests/Deal.Tests.Unit/Modules/Tenants/FakeTenantRegistry.cs b/src/core/tests/Deal.Tests.Unit/Modules/Tenants/FakeTenantRegistry.cs deleted file mode 100644 index 657d429..0000000 --- a/src/core/tests/Deal.Tests.Unit/Modules/Tenants/FakeTenantRegistry.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Deal.Modules.Tenants.Application.Abstractions; -using Deal.Modules.Tenants.Application.Models; - -namespace Deal.Tests.Unit.Modules.Tenants; - -/// -/// In-memory реализация для тестов gRPC-ингресса. -/// -public sealed class FakeTenantRegistry : ITenantRepository -{ - private readonly IReadOnlyList _tenants; - - /// - /// Создаёт реестр с фиксированным списком тенантов. - /// - /// Тенанты реестра (как строки public.tenants). - public FakeTenantRegistry(params TenantRecordDto[] tenants) - { - _tenants = tenants.ToList(); - } - - /// - public Task FindByIdAsync(Guid id, CancellationToken ct) => - Task.FromResult(_tenants.FirstOrDefault(tenant => tenant.Id == id)); - - /// - public Task> ListAsync(CancellationToken ct) => - Task.FromResult>(_tenants.ToList()); - - /// - public Task> ListPageAsync( - int offset, - int limit, - CancellationToken ct) - { - IReadOnlyList page = _tenants.Skip(offset).Take(limit).ToList(); - return Task.FromResult(page); - } - - /// - public Task CreateAsync(TenantRecordDto tenant, CancellationToken ct) => - throw new NotSupportedException("CreateAsync не используется тестами ингресса"); - - /// - public Task UpdateStatusAsync( - Guid id, - string status, - CancellationToken ct) => - throw new NotSupportedException("UpdateStatusAsync не используется тестами ингресса"); -} diff --git a/src/core/tests/Deal.Tests.Unit/Support/SourceIngressGrpcServiceTests.cs b/src/core/tests/Deal.Tests.Unit/Support/SourceIngressGrpcServiceTests.cs index b325247..8828b88 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/SourceIngressGrpcServiceTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/SourceIngressGrpcServiceTests.cs @@ -47,7 +47,7 @@ public sealed class SourceIngressGrpcServiceTests public async Task PushSource_ValidTenant_EnqueuesQueueRowAndAccepts() { var store = new FakePipelineStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync( registry, @@ -81,7 +81,7 @@ public sealed class SourceIngressGrpcServiceTests public async Task PushSource_SameSourceKeyTwice_SecondIsDuplicateAndQueueNotGrown() { var store = new FakePipelineStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync( registry, @@ -105,7 +105,7 @@ public sealed class SourceIngressGrpcServiceTests public async Task PushSource_EmptyText_NotAcceptedAndQueueNotGrown() { var store = new FakePipelineStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync( registry, @@ -127,7 +127,7 @@ public sealed class SourceIngressGrpcServiceTests public async Task PushSource_UnknownTenant_NotAcceptedWithoutRpcError() { var store = new FakePipelineStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync( registry, @@ -151,7 +151,7 @@ public sealed class SourceIngressGrpcServiceTests public async Task PushSource_WithoutToken_IsUnauthenticated() { var store = new FakePipelineStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync(registry, services => services.AddScoped(_ => store), async channel => { @@ -168,7 +168,7 @@ public sealed class SourceIngressGrpcServiceTests public async Task PushSource_WithWrongToken_IsUnauthenticated() { var store = new FakePipelineStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync(registry, services => services.AddScoped(_ => store), async channel => { @@ -185,7 +185,7 @@ public sealed class SourceIngressGrpcServiceTests public async Task PushSource_UnsetEnvToken_FailsClosed() { var store = new FakePipelineStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await TelegramIngressTestHost.RunAsync( serviceToken: null, @@ -209,7 +209,7 @@ public sealed class SourceIngressGrpcServiceTests public async Task PushSource_WithoutTenantIdMetadata_IsUnauthenticated() { var store = new FakePipelineStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync(registry, services => services.AddScoped(_ => store), async channel => { @@ -231,7 +231,7 @@ public sealed class SourceIngressGrpcServiceTests // registerTenantServices: Дополнительные tenant-scoped адаптеры сценария (IPipelineStore). // scenario: Сценарий с gRPC-каналом. private static Task RunAsync( - FakeTenantRegistry registry, + ITenantRepository registry, Action registerTenantServices, Func scenario) => TelegramIngressTestHost.RunAsync( diff --git a/src/core/tests/Deal.Tests.Unit/Support/TelegramIngressServiceTests.cs b/src/core/tests/Deal.Tests.Unit/Support/TelegramIngressServiceTests.cs index dc75a5e..d714d47 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/TelegramIngressServiceTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/TelegramIngressServiceTests.cs @@ -34,7 +34,7 @@ public sealed class TelegramIngressServiceTests [Fact] public async Task SyncDialogs_KnownTenant_AppliesCatalogAndReturnsMonitoredIds() { - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); var dialogs = new FakeTelegramStore(); await RunAsync( @@ -60,7 +60,7 @@ public sealed class TelegramIngressServiceTests [Fact] public async Task SyncDialogs_AutoMonitorNewDisabled_ReturnsEmptyMonitoredMirror() { - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); var settings = new FakeSettingsStore(); settings.Preload(SettingsKeys.AutoMonitorNew, "false"); var dialogs = new FakeTelegramStore(); @@ -92,7 +92,7 @@ public sealed class TelegramIngressServiceTests public async Task ReportStatus_PersistsStatusAndPublishesTransitionToasts() { var settings = new FakeSettingsStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); var broker = new SseBroker(); await RunAsync( @@ -139,7 +139,7 @@ public sealed class TelegramIngressServiceTests public async Task ReportStatus_UnknownTenant_NotSavedWithoutRpcError() { var settings = new FakeSettingsStore(); - var registry = new FakeTenantRegistry(Tenant(TenantA)); + var registry = TestRegistry.With(Tenant(TenantA)); await RunAsync( registry, @@ -178,7 +178,7 @@ public sealed class TelegramIngressServiceTests // (ISettingsStore/ITelegramStore/брокер). // scenario: Сценарий с gRPC-каналом. private static Task RunAsync( - FakeTenantRegistry registry, + ITenantRepository registry, Action registerTenantServices, Func scenario) => TelegramIngressTestHost.RunAsync( diff --git a/src/core/tests/Deal.Tests.Unit/Support/TestRegistry.cs b/src/core/tests/Deal.Tests.Unit/Support/TestRegistry.cs new file mode 100644 index 0000000..42c5851 --- /dev/null +++ b/src/core/tests/Deal.Tests.Unit/Support/TestRegistry.cs @@ -0,0 +1,27 @@ +using Deal.Modules.Tenants.Application.Abstractions; +using Deal.Modules.Tenants.Application.Models; +using NSubstitute; + +namespace Deal.Tests.Unit.Support; + +/// +/// Подставка реестра тенантов с фиксированным списком (для тестов gRPC-ингресса). +/// +public static class TestRegistry +{ + /// + /// Создаёт реестр-подставку: поиск/списки работают по заданному списку, запись не используется. + /// + /// Тенанты реестра (как строки public.tenants). + public static ITenantRepository With(params TenantRecordDto[] tenants) + { + var list = tenants.ToList(); + ITenantRepository registry = Substitute.For(); + registry.FindByIdAsync(Arg.Any(), Arg.Any()) + .Returns(ci => list.FirstOrDefault(t => t.Id == ci.Arg())); + registry.ListAsync(Arg.Any()).Returns(list); + registry.ListPageAsync(Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(ci => (IReadOnlyList)list.Skip(ci.ArgAt(0)).Take(ci.ArgAt(1)).ToList()); + return registry; + } +}