feat(auditlog): multi-value AuditLogQueryFilter dimensions
This commit is contained in:
@@ -91,7 +91,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0.AddMinutes(20)));
|
||||
|
||||
var rows = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(SourceSiteId: siteId),
|
||||
new AuditLogQueryFilter(SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(PageSize: 10));
|
||||
|
||||
Assert.Equal(3, rows.Count);
|
||||
@@ -114,13 +114,116 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0.AddMinutes(2), channel: AuditChannel.Notification));
|
||||
|
||||
var rows = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(Channel: AuditChannel.Notification, SourceSiteId: siteId),
|
||||
new AuditLogQueryFilter(
|
||||
Channels: new[] { AuditChannel.Notification },
|
||||
SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(PageSize: 10));
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
Assert.All(rows, r => Assert.Equal(AuditChannel.Notification, r.Channel));
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task QueryAsync_FilterByMultipleChannels_ReturnsUnion()
|
||||
{
|
||||
Skip.IfNot(_fixture.Available, _fixture.SkipReason);
|
||||
|
||||
var siteId = NewSiteId();
|
||||
await using var context = CreateContext();
|
||||
var repo = new AuditLogRepository(context);
|
||||
|
||||
var t0 = new DateTime(2026, 5, 2, 14, 0, 0, DateTimeKind.Utc);
|
||||
// One row per channel; the multi-value filter must return the union of
|
||||
// ApiOutbound + Notification and exclude DbOutbound.
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0, channel: AuditChannel.ApiOutbound));
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0.AddMinutes(1), channel: AuditChannel.Notification));
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0.AddMinutes(2), channel: AuditChannel.DbOutbound));
|
||||
|
||||
var rows = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(
|
||||
Channels: new[] { AuditChannel.ApiOutbound, AuditChannel.Notification },
|
||||
SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(PageSize: 10));
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
Assert.All(rows, r => Assert.Contains(r.Channel, new[] { AuditChannel.ApiOutbound, AuditChannel.Notification }));
|
||||
Assert.DoesNotContain(rows, r => r.Channel == AuditChannel.DbOutbound);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task QueryAsync_FilterByMultipleStatuses_ReturnsUnion()
|
||||
{
|
||||
Skip.IfNot(_fixture.Available, _fixture.SkipReason);
|
||||
|
||||
var siteId = NewSiteId();
|
||||
await using var context = CreateContext();
|
||||
var repo = new AuditLogRepository(context);
|
||||
|
||||
var t0 = new DateTime(2026, 5, 2, 15, 0, 0, DateTimeKind.Utc);
|
||||
// Failed + Parked are requested; Delivered must be excluded.
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0, status: AuditStatus.Failed));
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0.AddMinutes(1), status: AuditStatus.Parked));
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0.AddMinutes(2), status: AuditStatus.Delivered));
|
||||
|
||||
var rows = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(
|
||||
Statuses: new[] { AuditStatus.Failed, AuditStatus.Parked },
|
||||
SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(PageSize: 10));
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
Assert.All(rows, r => Assert.Contains(r.Status, new[] { AuditStatus.Failed, AuditStatus.Parked }));
|
||||
Assert.DoesNotContain(rows, r => r.Status == AuditStatus.Delivered);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task QueryAsync_FilterByMultipleSourceSiteIds_ReturnsUnion()
|
||||
{
|
||||
Skip.IfNot(_fixture.Available, _fixture.SkipReason);
|
||||
|
||||
var siteA = NewSiteId();
|
||||
var siteB = NewSiteId();
|
||||
var siteC = NewSiteId();
|
||||
await using var context = CreateContext();
|
||||
var repo = new AuditLogRepository(context);
|
||||
|
||||
var t0 = new DateTime(2026, 5, 2, 16, 0, 0, DateTimeKind.Utc);
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteA, occurredAtUtc: t0));
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteB, occurredAtUtc: t0.AddMinutes(1)));
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteC, occurredAtUtc: t0.AddMinutes(2)));
|
||||
|
||||
var rows = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(SourceSiteIds: new[] { siteA, siteB }),
|
||||
new AuditLogPaging(PageSize: 10));
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
Assert.All(rows, r => Assert.Contains(r.SourceSiteId, new[] { siteA, siteB }));
|
||||
Assert.DoesNotContain(rows, r => r.SourceSiteId == siteC);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task QueryAsync_EmptyChannelList_DoesNotConstrain()
|
||||
{
|
||||
Skip.IfNot(_fixture.Available, _fixture.SkipReason);
|
||||
|
||||
var siteId = NewSiteId();
|
||||
await using var context = CreateContext();
|
||||
var repo = new AuditLogRepository(context);
|
||||
|
||||
var t0 = new DateTime(2026, 5, 2, 17, 0, 0, DateTimeKind.Utc);
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0, channel: AuditChannel.ApiOutbound));
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(siteId, occurredAtUtc: t0.AddMinutes(1), channel: AuditChannel.Notification));
|
||||
|
||||
// An empty Channels list must mean "no filter" — NOT WHERE 1=0.
|
||||
var rows = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(
|
||||
Channels: Array.Empty<AuditChannel>(),
|
||||
SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(PageSize: 10));
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task QueryAsync_FilterBySourceSiteId()
|
||||
{
|
||||
@@ -137,7 +240,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
await repo.InsertIfNotExistsAsync(NewEvent(otherSiteId, occurredAtUtc: t0.AddMinutes(2)));
|
||||
|
||||
var rows = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(SourceSiteId: siteId),
|
||||
new AuditLogQueryFilter(SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(PageSize: 10));
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
@@ -160,7 +263,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
|
||||
var rows = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(
|
||||
SourceSiteId: siteId,
|
||||
SourceSiteIds: new[] { siteId },
|
||||
FromUtc: t0.AddMinutes(10),
|
||||
ToUtc: t0.AddHours(1)),
|
||||
new AuditLogPaging(PageSize: 10));
|
||||
@@ -187,7 +290,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
}
|
||||
|
||||
var page1 = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(SourceSiteId: siteId),
|
||||
new AuditLogQueryFilter(SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(PageSize: 2));
|
||||
|
||||
Assert.Equal(2, page1.Count);
|
||||
@@ -196,7 +299,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
|
||||
var cursor = page1[^1];
|
||||
var page2 = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(SourceSiteId: siteId),
|
||||
new AuditLogQueryFilter(SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(
|
||||
PageSize: 2,
|
||||
AfterOccurredAtUtc: cursor.OccurredAtUtc,
|
||||
@@ -208,7 +311,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
|
||||
var cursor2 = page2[^1];
|
||||
var page3 = await repo.QueryAsync(
|
||||
new AuditLogQueryFilter(SourceSiteId: siteId),
|
||||
new AuditLogQueryFilter(SourceSiteIds: new[] { siteId }),
|
||||
new AuditLogPaging(
|
||||
PageSize: 2,
|
||||
AfterOccurredAtUtc: cursor2.OccurredAtUtc,
|
||||
@@ -281,7 +384,7 @@ public class AuditLogRepositoryTests : IClassFixture<MsSqlMigrationFixture>
|
||||
await repo.InsertIfNotExistsAsync(e);
|
||||
}
|
||||
|
||||
var filter = new AuditLogQueryFilter(SourceSiteId: siteId);
|
||||
var filter = new AuditLogQueryFilter(SourceSiteIds: new[] { siteId });
|
||||
|
||||
var page1 = await repo.QueryAsync(filter, new AuditLogPaging(PageSize: 2));
|
||||
Assert.Equal(2, page1.Count);
|
||||
|
||||
Reference in New Issue
Block a user