Аудит: читаемые имена и «было → стало» вместо сырых данных #28
@@ -52,33 +52,38 @@ public static class AuditViewFactory
|
||||
return view;
|
||||
}
|
||||
|
||||
// Заменяет идентификаторы ссылок изменения значениями из карты имён.
|
||||
// Заменяет идентификаторы ссылок изменения значениями из карты имён и убирает пустые изменения.
|
||||
// record: Запись аудита.
|
||||
// references: Имена по идентификаторам ссылок.
|
||||
// Возвращает: Изменения с читаемыми значениями ссылок.
|
||||
// Возвращает: Изменения с читаемыми значениями ссылок без пар «без изменения».
|
||||
private static IReadOnlyList<AuditChangeDto> ResolveChanges(
|
||||
AuditRecordDto record,
|
||||
IReadOnlyDictionary<string, string> references)
|
||||
{
|
||||
IReadOnlyList<AuditChangeDto> changes = record.AuditChanges();
|
||||
if (references.Count == 0 || changes.Count == 0)
|
||||
{
|
||||
return changes;
|
||||
}
|
||||
|
||||
var resolved = new List<AuditChangeDto>(changes.Count);
|
||||
foreach (AuditChangeDto change in changes)
|
||||
{
|
||||
resolved.Add(change with
|
||||
string? from = ResolveReference(references, change.From);
|
||||
string? to = ResolveReference(references, change.To);
|
||||
if (IsUnchanged(from, to))
|
||||
{
|
||||
From = ResolveReference(references, change.From),
|
||||
To = ResolveReference(references, change.To),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
resolved.Add(change with { From = from, To = to });
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// Сравнивает значения «было» и «стало».
|
||||
// from: Значение до изменения.
|
||||
// to: Значение после изменения.
|
||||
// Возвращает: True — значения заданы и совпадают.
|
||||
private static bool IsUnchanged(string? from, string? to) =>
|
||||
from is not null && to is not null && string.Equals(from, to, StringComparison.Ordinal);
|
||||
|
||||
// Подставляет имя вместо идентификатора ссылки, если оно известно.
|
||||
// references: Имена по идентификаторам ссылок.
|
||||
// value: Значение изменения.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using Deal.Api.Services;
|
||||
using Deal.Modules.Tenants.Application.Models;
|
||||
using Deal.Tests.Unit.Support;
|
||||
|
||||
namespace Deal.Tests.Unit.Api;
|
||||
|
||||
/// <summary>
|
||||
/// Юнит-тесты проекции записей аудита
|
||||
/// </summary>
|
||||
public sealed class AuditViewFactoryTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Project_DropsUnchangedValues()
|
||||
{
|
||||
var record = new AuditRecordDto(
|
||||
AuditEvents.TenantLimitChanged,
|
||||
AuditActorTypes.Operator,
|
||||
ActorId: null,
|
||||
TenantId: null,
|
||||
Ip: null,
|
||||
DetailJson: """{"tenantId":"00000000-0000-0000-0000-000000000001","oldBudget":10000000,"oldPeriod":"month","budgetTokens":100000,"period":"month"}""");
|
||||
|
||||
IReadOnlyList<AuditRecordViewDto> view = await AuditViewFactory.ProjectAsync(
|
||||
[record],
|
||||
new TestTenantRepository().Repository,
|
||||
new TestAuthStore().Store,
|
||||
new TestAuditReferenceResolver(),
|
||||
CancellationToken.None);
|
||||
|
||||
AuditRecordViewDto row = Assert.Single(view);
|
||||
AuditChangeDto budget = Assert.Single(row.Changes);
|
||||
Assert.Equal("budget", budget.Field);
|
||||
Assert.Equal("10000000", budget.From);
|
||||
Assert.Equal("100000", budget.To);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Project_KeepsChangedValues()
|
||||
{
|
||||
var record = new AuditRecordDto(
|
||||
AuditEvents.SettingsUpdated,
|
||||
AuditActorTypes.Tenant,
|
||||
ActorId: null,
|
||||
TenantId: null,
|
||||
Ip: null,
|
||||
DetailJson: """{"changes":[{"field":"minLen","from":"24","to":"42"}]}""");
|
||||
|
||||
IReadOnlyList<AuditRecordViewDto> view = await AuditViewFactory.ProjectAsync(
|
||||
[record],
|
||||
new TestTenantRepository().Repository,
|
||||
new TestAuthStore().Store,
|
||||
new TestAuditReferenceResolver(),
|
||||
CancellationToken.None);
|
||||
|
||||
AuditChangeDto change = Assert.Single(Assert.Single(view).Changes);
|
||||
Assert.Equal("24", change.From);
|
||||
Assert.Equal("42", change.To);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user