Хелпер Support/TestRegistry.With: поиск/списки по фиксированному списку через Returns с Arg-матчерами. Потребители (3 файла) перетипизированы на ITenantRepository, фейк удалён, тесты 1340 зелёные.
This commit is contained in:
@@ -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<IServiceCollection> registerTenantServices,
|
||||
Func<GrpcChannel, Task> scenario,
|
||||
int permitsPerMinute = DefaultPermitsPerMinute)
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using Deal.Modules.Tenants.Application.Abstractions;
|
||||
using Deal.Modules.Tenants.Application.Models;
|
||||
|
||||
namespace Deal.Tests.Unit.Modules.Tenants;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory реализация <see cref="ITenantRepository"/> для тестов gRPC-ингресса.
|
||||
/// </summary>
|
||||
public sealed class FakeTenantRegistry : ITenantRepository
|
||||
{
|
||||
private readonly IReadOnlyList<TenantRecordDto> _tenants;
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт реестр с фиксированным списком тенантов.
|
||||
/// </summary>
|
||||
/// <param name="tenants">Тенанты реестра (как строки public.tenants).</param>
|
||||
public FakeTenantRegistry(params TenantRecordDto[] tenants)
|
||||
{
|
||||
_tenants = tenants.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<TenantRecordDto?> FindByIdAsync(Guid id, CancellationToken ct) =>
|
||||
Task.FromResult(_tenants.FirstOrDefault(tenant => tenant.Id == id));
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<TenantRecordDto>> ListAsync(CancellationToken ct) =>
|
||||
Task.FromResult<IReadOnlyList<TenantRecordDto>>(_tenants.ToList());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<TenantRecordDto>> ListPageAsync(
|
||||
int offset,
|
||||
int limit,
|
||||
CancellationToken ct)
|
||||
{
|
||||
IReadOnlyList<TenantRecordDto> page = _tenants.Skip(offset).Take(limit).ToList();
|
||||
return Task.FromResult(page);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateAsync(TenantRecordDto tenant, CancellationToken ct) =>
|
||||
throw new NotSupportedException("CreateAsync не используется тестами ингресса");
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> UpdateStatusAsync(
|
||||
Guid id,
|
||||
string status,
|
||||
CancellationToken ct) =>
|
||||
throw new NotSupportedException("UpdateStatusAsync не используется тестами ингресса");
|
||||
}
|
||||
@@ -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<IPipelineStore>(_ => 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<IPipelineStore>(_ => 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<IPipelineStore>(_ => 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<IServiceCollection> registerTenantServices,
|
||||
Func<GrpcChannel, Task> scenario)
|
||||
=> TelegramIngressTestHost.RunAsync(
|
||||
|
||||
@@ -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<IServiceCollection> registerTenantServices,
|
||||
Func<GrpcChannel, Task> scenario)
|
||||
=> TelegramIngressTestHost.RunAsync(
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using Deal.Modules.Tenants.Application.Abstractions;
|
||||
using Deal.Modules.Tenants.Application.Models;
|
||||
using NSubstitute;
|
||||
|
||||
namespace Deal.Tests.Unit.Support;
|
||||
|
||||
/// <summary>
|
||||
/// Подставка реестра тенантов с фиксированным списком (для тестов gRPC-ингресса).
|
||||
/// </summary>
|
||||
public static class TestRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Создаёт реестр-подставку: поиск/списки работают по заданному списку, запись не используется.
|
||||
/// </summary>
|
||||
/// <param name="tenants">Тенанты реестра (как строки public.tenants).</param>
|
||||
public static ITenantRepository With(params TenantRecordDto[] tenants)
|
||||
{
|
||||
var list = tenants.ToList();
|
||||
ITenantRepository registry = Substitute.For<ITenantRepository>();
|
||||
registry.FindByIdAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>())
|
||||
.Returns(ci => list.FirstOrDefault(t => t.Id == ci.Arg<Guid>()));
|
||||
registry.ListAsync(Arg.Any<CancellationToken>()).Returns(list);
|
||||
registry.ListPageAsync(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||
.Returns(ci => (IReadOnlyList<TenantRecordDto>)list.Skip(ci.ArgAt<int>(0)).Take(ci.ArgAt<int>(1)).ToList());
|
||||
return registry;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user