From 4a6fa374c5b5705f3dac3d589caf326616217c54 Mon Sep 17 00:00:00 2001 From: stepan Date: Sun, 13 Sep 2026 19:18:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B0=D1=83=D0=B4?= =?UTF-8?q?=D0=B8=D1=82=D0=B0=20=D0=BF=D0=BE=D0=B4=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BA=D1=82=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HTTP-тесты аудита и лимитов проверяют changes вместо плоского detailJson; подставки реестра умеют FindNamesByIdsAsync. Добавлены тесты разбора деталей (новый формат, старый плоский, повреждённый JSON) и проверка ответа аудита с именем пользователя и изменениями. --- .../Tenants/AuditRecordDtoExtensionsTests.cs | 95 +++++++++++++++++++ .../Modules/Tenants/AuditServiceTests.cs | 7 +- .../Tenants/SuspiciousActivityServiceTests.cs | 8 +- .../Support/JoinEndpointHttpTests.cs | 6 +- .../OperatorAnalyticsEndpointsHttpTests.cs | 2 +- .../OperatorAuditEndpointsHttpTests.cs | 46 ++++++++- .../OperatorInvitesEndpointsHttpTests.cs | 6 +- .../OperatorLimitsEndpointsHttpTests.cs | 22 ++--- .../OperatorSettingsEndpointsHttpTests.cs | 5 +- .../OperatorTenantsEndpointsHttpTests.cs | 31 ++---- .../Support/StorageTickSchedulerTests.cs | 6 ++ .../Support/TestTenantRepository.cs | 4 + .../Support/TestTenantStore.cs | 4 + 13 files changed, 183 insertions(+), 59 deletions(-) create mode 100644 src/core/tests/Deal.Tests.Unit/Modules/Tenants/AuditRecordDtoExtensionsTests.cs diff --git a/src/core/tests/Deal.Tests.Unit/Modules/Tenants/AuditRecordDtoExtensionsTests.cs b/src/core/tests/Deal.Tests.Unit/Modules/Tenants/AuditRecordDtoExtensionsTests.cs new file mode 100644 index 0000000..60790d4 --- /dev/null +++ b/src/core/tests/Deal.Tests.Unit/Modules/Tenants/AuditRecordDtoExtensionsTests.cs @@ -0,0 +1,95 @@ +using Deal.Modules.Tenants.Application.Models; +using Deal.Modules.Tenants.Application.Services; + +namespace Deal.Tests.Unit.Modules.Tenants; + +/// +/// Юнит-тесты разбора деталей аудита +/// +public sealed class AuditRecordDtoExtensionsTests +{ + [Fact] + public void AuditChanges_NewShape_ReturnsFieldFromTo() + { + var record = Record( + """{"changes":[{"field":"status","from":"active","to":"suspended"}]}"""); + + AuditChangeDto change = Assert.Single(record.AuditChanges()); + + Assert.Equal("status", change.Field); + Assert.Equal("active", change.From); + Assert.Equal("suspended", change.To); + } + + [Fact] + public void AuditChanges_NewShape_KeepsAbsentFromAsNull() + { + var record = Record("""{"changes":[{"field":"login","to":"admin"}]}"""); + + AuditChangeDto change = Assert.Single(record.AuditChanges()); + + Assert.Equal("login", change.Field); + Assert.Null(change.From); + Assert.Equal("admin", change.To); + } + + [Fact] + public void AuditChanges_LegacyFlat_PairsOldNewAndDropsTenantId() + { + var record = Record( + """{"tenantId":"6f3c0d1e-2b4a-4c8d-9e0f-1a2b3c4d5e6f","oldBudget":100,"newBudget":200,"name":"ten"}"""); + + IReadOnlyList changes = record.AuditChanges(); + + Assert.Collection( + changes, + budget => + { + Assert.Equal("budget", budget.Field); + Assert.Equal("100", budget.From); + Assert.Equal("200", budget.To); + }, + name => + { + Assert.Equal("name", name.Field); + Assert.Null(name.From); + Assert.Equal("ten", name.To); + }); + } + + [Fact] + public void AuditChanges_InvalidJson_ReturnsEmpty() + { + var record = Record("{ broken"); + + Assert.Empty(record.AuditChanges()); + } + + [Fact] + public void AuditChanges_NoDetails_ReturnsEmpty() + { + var record = Record(null); + + Assert.Empty(record.AuditChanges()); + } + + [Fact] + public void DetailValue_ReturnsToThenFrom() + { + var setRecord = Record(AuditService.ToDetailJson([AuditDetails.Set("login", "admin")])); + var changeRecord = Record(AuditService.ToDetailJson([AuditDetails.Change("login", "old", "new")])); + + Assert.Equal("admin", setRecord.DetailValue("login")); + Assert.Equal("new", changeRecord.DetailValue("login")); + Assert.Null(setRecord.DetailValue("missing")); + } + + private static AuditRecordDto Record(string? detailJson) => + new( + AuditEvents.TenantLoginOk, + AuditActorTypes.Tenant, + ActorId: null, + TenantId: null, + Ip: null, + DetailJson: detailJson); +} diff --git a/src/core/tests/Deal.Tests.Unit/Modules/Tenants/AuditServiceTests.cs b/src/core/tests/Deal.Tests.Unit/Modules/Tenants/AuditServiceTests.cs index 2d01bf0..e76bec2 100644 --- a/src/core/tests/Deal.Tests.Unit/Modules/Tenants/AuditServiceTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Modules/Tenants/AuditServiceTests.cs @@ -109,11 +109,12 @@ public sealed class AuditServiceTests } [Fact] - public void ToDetailJson_SerializesCamelCase() + public void ToDetailJson_SerializesChangeFields() { - string json = AuditService.ToDetailJson(new { login = "Admin" }); + string json = AuditService.ToDetailJson([AuditDetails.Set("login", "Admin")]); - Assert.Contains("\"login\":\"Admin\"", json); + Assert.Contains("\"field\":\"login\"", json); + Assert.Contains("\"to\":\"Admin\"", json); } [Fact] diff --git a/src/core/tests/Deal.Tests.Unit/Modules/Tenants/SuspiciousActivityServiceTests.cs b/src/core/tests/Deal.Tests.Unit/Modules/Tenants/SuspiciousActivityServiceTests.cs index c54c787..5c8cfc5 100644 --- a/src/core/tests/Deal.Tests.Unit/Modules/Tenants/SuspiciousActivityServiceTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Modules/Tenants/SuspiciousActivityServiceTests.cs @@ -96,7 +96,7 @@ public sealed class SuspiciousActivityServiceTests ActorId: actor, TenantId: Tenant, Ip: $"10.1.0.{i + 1}", - DetailJson: AuditService.ToDetailJson(new { login = "user" }), + DetailJson: AuditService.ToDetailJson([AuditDetails.Set("login", "user")]), At: Now.AddMinutes(-i)), CancellationToken.None); } @@ -124,7 +124,7 @@ public sealed class SuspiciousActivityServiceTests ActorId: null, TenantId: Tenant, Ip: $"10.2.0.{i + 1}", - DetailJson: AuditService.ToDetailJson(new { login = $"user{i}" }), + DetailJson: AuditService.ToDetailJson([AuditDetails.Set("login", $"user{i}")]), At: Now.AddMinutes(-i)), CancellationToken.None); } @@ -252,7 +252,7 @@ public sealed class SuspiciousActivityServiceTests ActorId: null, TenantId: null, Ip: ip, - DetailJson: AuditService.ToDetailJson(new { login }), + DetailJson: AuditService.ToDetailJson([AuditDetails.Set("login", login)]), At: Now.AddMinutes(-minutesAgo)), CancellationToken.None); } @@ -271,7 +271,7 @@ public sealed class SuspiciousActivityServiceTests ActorId: null, TenantId: null, Ip: ip, - DetailJson: AuditService.ToDetailJson(new { login }), + DetailJson: AuditService.ToDetailJson([AuditDetails.Set("login", login)]), At: Now.AddMinutes(-minutesAgo)), CancellationToken.None); } diff --git a/src/core/tests/Deal.Tests.Unit/Support/JoinEndpointHttpTests.cs b/src/core/tests/Deal.Tests.Unit/Support/JoinEndpointHttpTests.cs index 220e1cf..34073ac 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/JoinEndpointHttpTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/JoinEndpointHttpTests.cs @@ -307,10 +307,8 @@ public sealed class JoinEndpointHttpTests string code) { Assert.NotNull(record.DetailJson); - using var document = JsonDocument.Parse(record.DetailJson!); - JsonElement root = document.RootElement; - Assert.Equal(email, root.GetProperty("email").GetString()); - Assert.Equal(SessionTokens.HashToken(code), root.GetProperty("codeHash").GetString()); + Assert.Equal(email, record.DetailValue("email")); + Assert.Equal(SessionTokens.HashToken(code), record.DetailValue("codeHash")); } diff --git a/src/core/tests/Deal.Tests.Unit/Support/OperatorAnalyticsEndpointsHttpTests.cs b/src/core/tests/Deal.Tests.Unit/Support/OperatorAnalyticsEndpointsHttpTests.cs index d4379b6..909fac6 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/OperatorAnalyticsEndpointsHttpTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/OperatorAnalyticsEndpointsHttpTests.cs @@ -93,7 +93,7 @@ public sealed class OperatorAnalyticsEndpointsHttpTests ActorId: null, TenantId: ActiveTenant, Ip: "10.9.0.1", - DetailJson: AuditService.ToDetailJson(new { login = "brute" }), + DetailJson: AuditService.ToDetailJson([AuditDetails.Set("login", "brute")]), At: DateTimeOffset.UtcNow.AddMinutes(-i)), CancellationToken.None); } diff --git a/src/core/tests/Deal.Tests.Unit/Support/OperatorAuditEndpointsHttpTests.cs b/src/core/tests/Deal.Tests.Unit/Support/OperatorAuditEndpointsHttpTests.cs index 4c80ebe..3fdef10 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/OperatorAuditEndpointsHttpTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/OperatorAuditEndpointsHttpTests.cs @@ -2,6 +2,7 @@ using System.Net; using System.Text; using System.Text.Json; using Deal.Modules.Tenants.Application.Models; +using Deal.Modules.Tenants.Application.Services; using Deal.Tests.Unit.Modules.Tenants; using Deal.Tests.Unit.Support; @@ -276,6 +277,46 @@ public sealed class OperatorAuditEndpointsHttpTests auditStore); } + [Fact] + public async Task Audit_ReturnsTenantNameAndHumanReadableChanges() + { + var tenantId = Guid.NewGuid(); + var auditStore = new TestAuditLogStore(); + await auditStore.Store.AppendAsync( + new AuditRecordDto( + AuditEvents.TenantStatusChanged, + AuditActorTypes.Operator, + ActorId: Guid.NewGuid(), + TenantId: tenantId, + Ip: null, + DetailJson: AuditService.ToDetailJson([AuditDetails.Change("status", "active", "suspended")]), + At: DateTimeOffset.UtcNow), + CancellationToken.None); + + await OperatorAuthHttpHost.RunAsync( + NewOperatorStore(), + NewUserStore(), + async (baseAddress, _, _, _, _, _) => + { + HttpClient operatorClient = CreateClient(baseAddress); + await LoginOperatorAsync(operatorClient, baseAddress); + + using HttpResponseMessage response = await operatorClient.GetAsync($"{baseAddress}/api/operator/audit?eventType=tenant_status_changed"); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + JsonElement item = (await ReadJsonAsync(response)).GetProperty("items")[0]; + Assert.Equal(tenantId, item.GetProperty("tenantId").GetGuid()); + Assert.Equal("acme", item.GetProperty("tenantName").GetString()); + JsonElement change = item.GetProperty("changes")[0]; + Assert.Equal("status", change.GetProperty("field").GetString()); + Assert.Equal("active", change.GetProperty("from").GetString()); + Assert.Equal("suspended", change.GetProperty("to").GetString()); + }, + auditStore, + tenantStore: new TestTenantStore( + new TenantRecordDto(tenantId, "acme", TenantStatuses.Active, DateTimeOffset.UtcNow))); + } + // ─── Хелперы ───────────────────────────────────────────────────────── // Фейк-хранилище оператора с активным operator/operator. @@ -346,10 +387,11 @@ public sealed class OperatorAuditEndpointsHttpTests Assert.Contains(IpPrefix, record.Ip); } - // Проверяет login попытки в DetailJson записи (без пароля). + // Проверяет логин попытки в деталях записи (без пароля). private static void AssertDetailLogin(AuditRecordDto record, string login) { Assert.NotNull(record.DetailJson); - Assert.Contains($"\"login\":\"{login}\"", record.DetailJson); + Assert.Contains("\"field\":\"login\"", record.DetailJson); + Assert.Contains($"\"to\":\"{login}\"", record.DetailJson); } } diff --git a/src/core/tests/Deal.Tests.Unit/Support/OperatorInvitesEndpointsHttpTests.cs b/src/core/tests/Deal.Tests.Unit/Support/OperatorInvitesEndpointsHttpTests.cs index 4c99114..fb01b52 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/OperatorInvitesEndpointsHttpTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/OperatorInvitesEndpointsHttpTests.cs @@ -247,10 +247,8 @@ public sealed class OperatorInvitesEndpointsHttpTests string code) { Assert.NotNull(record.DetailJson); - using var document = JsonDocument.Parse(record.DetailJson!); - JsonElement root = document.RootElement; - Assert.Equal(email, root.GetProperty("email").GetString()); - Assert.Equal(SessionTokens.HashToken(code), root.GetProperty("codeHash").GetString()); + Assert.Equal(email, record.DetailValue("email")); + Assert.Equal(SessionTokens.HashToken(code), record.DetailValue("codeHash")); } // Логинит оператора (ожидается 200). diff --git a/src/core/tests/Deal.Tests.Unit/Support/OperatorLimitsEndpointsHttpTests.cs b/src/core/tests/Deal.Tests.Unit/Support/OperatorLimitsEndpointsHttpTests.cs index 7bee4dd..738cb62 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/OperatorLimitsEndpointsHttpTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/OperatorLimitsEndpointsHttpTests.cs @@ -150,14 +150,12 @@ public sealed class OperatorLimitsEndpointsHttpTests Assert.Equal(AuditActorTypes.Operator, audit.ActorType); Assert.Equal(operatorStore.Operators.Single().Id, audit.ActorId); Assert.Equal(FirstTenantId, audit.TenantId); - Assert.NotNull(audit.DetailJson); - using (var detail = JsonDocument.Parse(audit.DetailJson!)) - { - Assert.Equal(DefaultBudgetTokens, detail.RootElement.GetProperty("oldBudget").GetInt64()); - Assert.Equal(TenantLimitPeriods.Month, detail.RootElement.GetProperty("oldPeriod").GetString()); - Assert.Equal(newBudget, detail.RootElement.GetProperty("budgetTokens").GetInt64()); - Assert.Equal(TenantLimitPeriods.Month, detail.RootElement.GetProperty("period").GetString()); - } + AuditChangeDto budget = audit.AuditChanges().Single(c => c.Field == "budget"); + Assert.Equal(DefaultBudgetTokens.ToString(), budget.From); + Assert.Equal(newBudget.ToString(), budget.To); + AuditChangeDto period = audit.AuditChanges().Single(c => c.Field == "period"); + Assert.Equal(TenantLimitPeriods.Month, period.From); + Assert.Equal(TenantLimitPeriods.Month, period.To); }, auditStore); } @@ -182,12 +180,8 @@ public sealed class OperatorLimitsEndpointsHttpTests AuditRecordDto audit = Assert.Single(auditStore.Records, r => r.EventType == AuditEvents.TenantLimitChanged); Assert.Equal(operatorStore.Operators.Single().Id, audit.ActorId); - Assert.NotNull(audit.DetailJson); - using (var detail = JsonDocument.Parse(audit.DetailJson!)) - { - Assert.Equal(DefaultBudgetTokens, detail.RootElement.GetProperty("budgetTokens").GetInt64()); - Assert.Equal(TenantLimitPeriods.Day, detail.RootElement.GetProperty("period").GetString()); - } + Assert.Equal(DefaultBudgetTokens.ToString(), audit.AuditChanges().Single(c => c.Field == "budget").To); + Assert.Equal(TenantLimitPeriods.Day, audit.AuditChanges().Single(c => c.Field == "period").To); }, auditStore); } diff --git a/src/core/tests/Deal.Tests.Unit/Support/OperatorSettingsEndpointsHttpTests.cs b/src/core/tests/Deal.Tests.Unit/Support/OperatorSettingsEndpointsHttpTests.cs index 4280c87..c099a53 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/OperatorSettingsEndpointsHttpTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/OperatorSettingsEndpointsHttpTests.cs @@ -89,9 +89,8 @@ public sealed class OperatorSettingsEndpointsHttpTests Assert.Null(record.TenantId); Assert.NotNull(record.DetailJson); Assert.DoesNotContain("abcdefghijklmnop", record.DetailJson); - using var detail = JsonDocument.Parse(record.DetailJson!); - Assert.Equal("1234567", detail.RootElement.GetProperty("apiId").GetString()); - Assert.True(detail.RootElement.GetProperty("apiHashSet").GetBoolean()); + Assert.Equal("1234567", record.DetailValue("apiId")); + Assert.Equal("true", record.DetailValue("apiHashSet")); }, auditStore); } diff --git a/src/core/tests/Deal.Tests.Unit/Support/OperatorTenantsEndpointsHttpTests.cs b/src/core/tests/Deal.Tests.Unit/Support/OperatorTenantsEndpointsHttpTests.cs index 5579f7f..08741a4 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/OperatorTenantsEndpointsHttpTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/OperatorTenantsEndpointsHttpTests.cs @@ -126,11 +126,7 @@ public sealed class OperatorTenantsEndpointsHttpTests Assert.Equal(operatorStore.Operators.Single().Id, createdAudit.ActorId); Assert.Equal(createdId, createdAudit.TenantId); Assert.NotNull(createdAudit.DetailJson); - using (var detail = JsonDocument.Parse(createdAudit.DetailJson!)) - { - Assert.Equal(createdId, detail.RootElement.GetProperty("tenantId").GetGuid()); - Assert.Equal(NewTenantName, detail.RootElement.GetProperty("name").GetString()); - } + Assert.Equal(NewTenantName, createdAudit.DetailValue("name")); }, auditStore); } @@ -367,11 +363,7 @@ public sealed class OperatorTenantsEndpointsHttpTests Assert.Equal(operatorStore.Operators.Single().Id, started.ActorId); Assert.Equal(TenantId, started.TenantId); Assert.NotNull(started.DetailJson); - using (var detail = JsonDocument.Parse(started.DetailJson!)) - { - Assert.Equal(TenantUserLogin, detail.RootElement.GetProperty("targetLogin").GetString()); - Assert.Equal(TenantId, detail.RootElement.GetProperty("tenantId").GetGuid()); - } + Assert.Equal(TenantUserLogin, started.DetailValue("targetLogin")); HttpClient userClient = CreateClient(baseAddress); using (HttpResponseMessage me = await GetWithTenantCookieAsync(userClient, baseAddress, sessionToken, "/api/auth/me")) @@ -514,26 +506,17 @@ public sealed class OperatorTenantsEndpointsHttpTests Assert.Equal(AuditActorTypes.Operator, record.ActorType); Assert.Equal(operatorStore.Operators.Single().Id, record.ActorId); Assert.Equal(TenantId, record.TenantId); - Assert.NotNull(record.DetailJson); - using var detail = JsonDocument.Parse(record.DetailJson!); - Assert.Equal(TenantId, detail.RootElement.GetProperty("tenantId").GetGuid()); - Assert.Equal(status, detail.RootElement.GetProperty("status").GetString()); + Assert.Equal(status, record.DetailValue("status")); } - // Статус из DetailJson записи tenant_status_changed. - private static string? StatusOf(AuditRecordDto record) - { - Assert.NotNull(record.DetailJson); - using var detail = JsonDocument.Parse(record.DetailJson!); - return detail.RootElement.GetProperty("status").GetString(); - } + // Статус из деталей записи tenant_status_changed. + private static string? StatusOf(AuditRecordDto record) => record.DetailValue("status"); - // Проверяет DetailJson записи аудита: поле login. + // Проверяет детали записи аудита: параметр login. private static void AssertDetailLogin(AuditRecordDto record, string login) { Assert.NotNull(record.DetailJson); - using var detail = JsonDocument.Parse(record.DetailJson!); - Assert.Equal(login, detail.RootElement.GetProperty("login").GetString()); + Assert.Equal(login, record.DetailValue("login")); } // GET с tenant-кукой deal_session=rawToken (impersonation-токен как значение куки). diff --git a/src/core/tests/Deal.Tests.Unit/Support/StorageTickSchedulerTests.cs b/src/core/tests/Deal.Tests.Unit/Support/StorageTickSchedulerTests.cs index 95198d0..5f54613 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/StorageTickSchedulerTests.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/StorageTickSchedulerTests.cs @@ -457,6 +457,12 @@ public sealed class StorageTickSchedulerTests CancellationToken ct) => throw new InvalidOperationException("реестр тенантов недоступен"); + /// + public Task> FindNamesByIdsAsync( + IReadOnlyCollection ids, + CancellationToken ct) => + throw new InvalidOperationException("реестр тенантов недоступен"); + /// public Task FindByIdAsync(Guid id, CancellationToken ct) => throw new NotSupportedException(); diff --git a/src/core/tests/Deal.Tests.Unit/Support/TestTenantRepository.cs b/src/core/tests/Deal.Tests.Unit/Support/TestTenantRepository.cs index 42a5c99..6ca16af 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/TestTenantRepository.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/TestTenantRepository.cs @@ -38,6 +38,10 @@ public sealed class TestTenantRepository .Do(ci => _pageRequests.Add((ci.ArgAt(0), ci.ArgAt(1)))); Repository.ListPageAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(ci => (IReadOnlyList)_tenants.Skip(ci.ArgAt(0)).Take(ci.ArgAt(1)).ToList()); + Repository.FindNamesByIdsAsync(Arg.Any>(), Arg.Any()) + .Returns(ci => (IReadOnlyDictionary)_tenants + .Where(tenant => ci.ArgAt>(0).Contains(tenant.Id)) + .ToDictionary(tenant => tenant.Id, tenant => tenant.Name)); Repository.FindByIdAsync(Arg.Any(), Arg.Any()) .Returns(_ => throw new NotSupportedException("FindByIdAsync не используется тестами реестра")); Repository.UpdateStatusAsync(Arg.Any(), Arg.Any(), Arg.Any()) diff --git a/src/core/tests/Deal.Tests.Unit/Support/TestTenantStore.cs b/src/core/tests/Deal.Tests.Unit/Support/TestTenantStore.cs index 2925ec6..7a59059 100644 --- a/src/core/tests/Deal.Tests.Unit/Support/TestTenantStore.cs +++ b/src/core/tests/Deal.Tests.Unit/Support/TestTenantStore.cs @@ -39,6 +39,10 @@ public sealed class TestTenantStore .Returns(_tenants); Repository.ListPageAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(ci => (IReadOnlyList)_tenants.Skip(ci.ArgAt(0)).Take(ci.ArgAt(1)).ToList()); + Repository.FindNamesByIdsAsync(Arg.Any>(), Arg.Any()) + .Returns(ci => (IReadOnlyDictionary)_tenants + .Where(tenant => ci.ArgAt>(0).Contains(tenant.Id)) + .ToDictionary(tenant => tenant.Id, tenant => tenant.Name)); Repository.UpdateStatusAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(ci => {