Purge audit: Detail carries window and count, never the network tag the member dashboard would render
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
abb0986508
commit
162d7b1f0d
3 changed files with 59 additions and 3 deletions
|
|
@ -158,13 +158,14 @@ public sealed class ConceptAppraisalService(
|
|||
.ToListAsync(cancellationToken);
|
||||
dbContext.ConceptAppraisals.RemoveRange(rows);
|
||||
|
||||
// The audit write saves the removals in the same SaveChanges. Tag, window and count only; no voter keys.
|
||||
// The audit write saves the removals in the same SaveChanges. The member dashboard renders Detail, so it
|
||||
// carries the window and count only: no network tag and no voter keys. The actor and window audit the purge.
|
||||
await auditTrail.RecordAsync(
|
||||
actor.UserAccount.Id,
|
||||
nameof(ConceptAppraisal),
|
||||
Guid.Empty,
|
||||
"Purge",
|
||||
$"tag={networkTag} from={fromUtc:O} to={toUtc:O} count={rows.Count}",
|
||||
$"from={fromUtc:O} to={toUtc:O} count={rows.Count}",
|
||||
cancellationToken);
|
||||
return rows.Count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -290,7 +290,10 @@ public sealed class ConceptAppraisalServiceTests
|
|||
var audit = Assert.Single(f.Db.AuditEvents);
|
||||
Assert.Equal("ConceptAppraisal", audit.EntityType);
|
||||
Assert.Equal("Purge", audit.Action);
|
||||
Assert.Contains(tag, audit.Detail);
|
||||
Assert.DoesNotContain(tag, audit.Detail);
|
||||
Assert.DoesNotContain("tag", audit.Detail);
|
||||
Assert.Equal(admin.UserAccount!.Id, audit.ActorUserAccountId);
|
||||
Assert.Contains("from=" + t0.ToString("O"), audit.Detail);
|
||||
Assert.Contains("count=1", audit.Detail);
|
||||
Assert.DoesNotContain("device", audit.Detail);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
using System.Net;
|
||||
using Bifrost.Web.Configuration;
|
||||
using Bifrost.Web.Features.ConceptVotes;
|
||||
using Bifrost.Web.Features.Shared;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Bifrost.Web.Data;
|
||||
using Bifrost.Web.Domain;
|
||||
using Bifrost.Web.Tests.Support;
|
||||
|
|
@ -143,4 +148,51 @@ public sealed class MemberConsoleTests : IClassFixture<TestWebApplicationFactory
|
|||
Assert.Contains("Draft motion implementation PR", html);
|
||||
Assert.Contains("nibu", html);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Member_console_activity_shows_a_purge_without_its_network_tag()
|
||||
{
|
||||
using (var warmupClient = _factory.CreateClient())
|
||||
{
|
||||
_ = await warmupClient.GetAsync("/App");
|
||||
}
|
||||
|
||||
string tag;
|
||||
using (var scope = _factory.Services.CreateScope())
|
||||
{
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<BifrostDbContext>();
|
||||
var admin = await dbContext.UserAccounts
|
||||
.Include(x => x.Membership)
|
||||
.SingleAsync(x => x.NormalizedGitHubLogin == "TEST-ADMIN");
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
dbContext.ConceptSubjects.Add(new ConceptSubject
|
||||
{
|
||||
Collection = "aetheria-concept-mill",
|
||||
Key = "ship-a",
|
||||
Title = "ship-a",
|
||||
RegisteredAtUtc = now
|
||||
});
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
var service = scope.ServiceProvider.GetRequiredService<ConceptAppraisalService>();
|
||||
var options = scope.ServiceProvider.GetRequiredService<IOptions<ConceptVoteOptions>>().Value;
|
||||
var client = System.Net.IPAddress.Parse("203.0.113.7");
|
||||
tag = NetworkPrefix.Tag(client, options.IPv4PrefixLength, options.IPv6PrefixLength, options.NetworkTagSecret);
|
||||
var voter = VoterIdentity.For(CurrentBifrostActor.Anonymous, "member-view-device")!;
|
||||
await service.CastAsync("aetheria-concept-mill", "ship-a", voter, ConceptStance.Up, client, default);
|
||||
|
||||
var removed = await service.PurgeAsync(
|
||||
new CurrentBifrostActor(true, admin), tag, now.AddHours(-1), now.AddHours(1), default);
|
||||
Assert.Equal(1, removed);
|
||||
}
|
||||
|
||||
using var memberClient = _factory.CreateClient();
|
||||
using var response = await memberClient.GetAsync("/App");
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Contains("Purge", html);
|
||||
Assert.Contains("count", html);
|
||||
Assert.DoesNotContain(tag, html);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue