Skip to content
This repository was archived by the owner on Feb 20, 2025. It is now read-only.

Commit cd2be1e

Browse files
committed
WIP
1 parent 30893d0 commit cd2be1e

File tree

2 files changed

+62
-30
lines changed

2 files changed

+62
-30
lines changed

src/Shiny.Extensions.EntityFramework/Auditing/AuditSaveChangesInterceptor.cs

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
namespace Shiny.Auditing;
88

99

10+
// TODO: I need the entity ID after insert
11+
// TODO: catch ExecuteUpdate & ExecuteDelete - how? ExecuteDelete isn't something I believe in with audited tables anyhow - So only ExecuteUpdate
1012
public class AuditSaveChangesInterceptor(IAuditInfoProvider provider) : SaveChangesInterceptor
1113
{
1214
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
@@ -22,6 +24,7 @@ public override InterceptionResult<int> SavingChanges(DbContextEventData eventDa
2224
eventData.Context!.AddRange(entries);
2325
return base.SavingChanges(eventData, result);
2426
}
27+
2528

2629
static DbOperation ToOperation(EntityState state)
2730
{
@@ -33,6 +36,7 @@ static DbOperation ToOperation(EntityState state)
3336

3437
return DbOperation.Update;
3538
}
39+
3640

3741
protected virtual List<AuditEntry> GetAuditEntries(DbContextEventData eventData)
3842
{
@@ -114,6 +118,7 @@ protected virtual bool IsAuditedProperty(PropertyEntry entry)
114118
{
115119
nameof(IAuditable.LastEditUserIdentifier) => true,
116120
nameof(IAuditable.DateCreated) => true,
121+
nameof(IAuditable.DateUpdated) => true,
117122
_ => false
118123
};
119124
}

tests/Shiny.Extensions.EntityFramework.Tests/AuditTests.cs

+57-30
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
namespace Shiny.Extensions.EntityFramework.Tests;
88

99

10-
// TODO: test on entity updates
11-
// TODO: test ignored properties
10+
// TODO: check entityId & entityType
1211
public class AuditTests : IDisposable
1312
{
1413
readonly TestAuditInfoProvider auditProvider;
@@ -45,36 +44,15 @@ async Task DoDb(Func<TestDbContext, Task> task)
4544
[Fact]
4645
public async Task AddAudits()
4746
{
48-
using var scope = this.serviceProvider.CreateScope();
49-
var data = scope.ServiceProvider.GetRequiredService<TestDbContext>();
50-
await this.DoDb(data =>
51-
{
52-
var manu = new Manufacturer { Name = "Cadillac" };
53-
data.Add(manu);
54-
55-
var model = new Model
56-
{
57-
Name = "X5",
58-
Manufacturer = manu
59-
};
60-
data.Add(model);
61-
return data.SaveChangesAsync();
62-
});
47+
await this.Seed();
6348

64-
6549
await this.DoDb(async data =>
6650
{
6751
var audits = await data.AuditEntries.ToListAsync();
6852
audits.Count.Should().Be(2);
69-
70-
audits.All(x => x.UserIdentifier!.Equals("Test User")).Should().BeTrue("All audits should be 'Test User'");
71-
audits.All(x => x.Tenant!.Equals("Test Tenant")).Should().BeTrue("All audits should be 'Test Tenant'");
72-
audits.All(x => x.UserIpAddress!.Equals("0.0.0.0")).Should().BeTrue("All audits should be '0.0.0.0'");
73-
audits.All(x => x.AppLocation!.Equals("UNIT TESTS")).Should().BeTrue("All audits should be 'UNIT TESTS'");
74-
75-
audits.All(x => x.Operation == DbOperation.Insert).Should().BeTrue("All audits should Insert operations");
53+
audits.ForEach(x => AssertAudit(x, DbOperation.Insert));
7654

77-
var manu = await data.Manufacturers.FirstOrDefaultAsync();
55+
var manu = await data.Manufacturers.FirstAsync()!;
7856
manu.LastEditUserIdentifier.Should().Be("Test User");
7957
manu.DateUpdated.Date.Should().Be(DateTimeOffset.UtcNow.Date, "DateUpdated is wrong");
8058
manu.DateCreated.Date.Should().Be(DateTimeOffset.UtcNow.Date, "DateCreated is wrong");
@@ -85,20 +63,69 @@ await this.DoDb(async data =>
8563
[Fact]
8664
public async Task DeleteAudits()
8765
{
88-
66+
await this.Seed();
67+
await this.DoDb(async data =>
68+
{
69+
var manu = await data.Manufacturers.FirstAsync();
70+
data.Remove(manu);
71+
await data.SaveChangesAsync();
72+
});
73+
await this.DoDb(async data =>
74+
{
75+
var audit = await data.AuditEntries.FirstOrDefaultAsync(x => x.Operation == DbOperation.Delete);
76+
audit.Should().NotBeNull("No Delete Audit Found");
77+
AssertAudit(audit!, DbOperation.Delete);
78+
79+
// TODO: check changeset
80+
});
8981
}
9082

9183

9284
[Fact]
9385
public async Task UpdateAudits()
9486
{
95-
87+
await this.Seed();
88+
await this.DoDb(async data =>
89+
{
90+
var manu = await data.Manufacturers.FirstAsync();
91+
manu.Name = "UPDATE";
92+
await data.SaveChangesAsync();
93+
});
94+
await this.DoDb(async data =>
95+
{
96+
var audit = await data.AuditEntries.FirstOrDefaultAsync(x => x.Operation == DbOperation.Update);
97+
audit.Should().NotBeNull("No Delete Audit Found");
98+
AssertAudit(audit!, DbOperation.Update);
99+
100+
// TODO: check changeset
101+
});
96102
}
97103

98-
public void Dispose()
104+
105+
Task Seed() => this.DoDb(data =>
99106
{
100-
(this.serviceProvider as IDisposable)?.Dispose();
107+
var manu = new Manufacturer { Name = "Cadillac" };
108+
data.Add(manu);
109+
110+
var model = new Model
111+
{
112+
Name = "X5",
113+
Manufacturer = manu
114+
};
115+
data.Add(model);
116+
return data.SaveChangesAsync();
117+
});
118+
119+
120+
void AssertAudit(AuditEntry audit, DbOperation op)
121+
{
122+
audit.Operation.Should().Be(op, "Invalid Operation");
123+
audit.UserIdentifier.Should().Be("Test User");
124+
audit.UserIpAddress.Should().Be("0.0.0.0");
125+
audit.AppLocation.Should().Be("UNIT TESTS");
101126
}
127+
128+
public void Dispose() => (this.serviceProvider as IDisposable)?.Dispose();
102129
}
103130

104131
public class TestAuditInfoProvider : IAuditInfoProvider

0 commit comments

Comments
 (0)