Resolve all 46 low-severity architecture review findings

This commit is contained in:
Joseph Doherty
2026-09-09 22:56:36 -04:00
parent fef3e7675f
commit 9ee402bbcd
139 changed files with 2621 additions and 1082 deletions
@@ -1,3 +1,4 @@
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.AuditLog.Site;
@@ -32,6 +33,73 @@ public class SqliteAuditWriterReadByExecutionTests : IDisposable
ScadaBridgeAuditEventFactory.Create(AuditChannel.ScriptTrace, kind, AuditStatus.Delivered,
occurredAtUtc: at, executionId: exec, parentExecutionId: parent, sourceSiteId: "site-a");
[Theory]
[InlineData(1)]
[InlineData(64)]
public async Task TraceReadPlan_UsesExecutionIndexesInsteadOfRetainedStoreScan(int idCount)
{
await using var writer = CreateWriter();
using var connection = new SqliteConnection($"Data Source={_dbPath}");
await connection.OpenAsync();
using var command = connection.CreateCommand();
var names = Enumerable.Range(0, idCount).Select(i => $"$e{i}").ToArray();
command.CommandText = "EXPLAIN QUERY PLAN " + SqliteAuditWriter.BuildTraceReadSql(string.Join(", ", names));
foreach (var name in names) command.Parameters.AddWithValue(name, Guid.NewGuid().ToString("D"));
command.Parameters.AddWithValue("$since", DateTime.MinValue.ToString("o"));
command.Parameters.AddWithValue("$after", DBNull.Value);
command.Parameters.AddWithValue("$limit", 2000);
var plan = new List<string>();
using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync()) plan.Add(reader.GetString(3));
Assert.Contains(plan, p => p.Contains("SEARCH audit_event USING INDEX IX_audit_event_execution", StringComparison.Ordinal));
Assert.Contains(plan, p => p.Contains("SEARCH audit_event USING INDEX IX_audit_event_parent_execution", StringComparison.Ordinal));
Assert.DoesNotContain(plan, p => p.StartsWith("SCAN audit_event", StringComparison.Ordinal));
}
[Fact]
public async Task LegacyMalformedDetails_DoNotBreakStartupOrTraceReads()
{
var root = Guid.NewGuid();
var row = Row(T0, root, null);
await using (var writer = CreateWriter()) await writer.WriteAsync(row);
using (var connection = new SqliteConnection($"Data Source={_dbPath}"))
{
await connection.OpenAsync();
using var command = connection.CreateCommand();
// Simulate a pre-upgrade file, including malformed payloads from an older writer.
command.CommandText = "DROP INDEX IF EXISTS IX_audit_event_execution; DROP INDEX IF EXISTS IX_audit_event_parent_execution; "
+ "INSERT INTO audit_event(EventId,OccurredAtUtc,Actor,Action,Outcome,DetailsJson) VALUES ($id,$at,'legacy','legacy','Success','{broken');";
command.Parameters.AddWithValue("$id", Guid.NewGuid().ToString("D"));
command.Parameters.AddWithValue("$at", T0.ToString("o"));
await command.ExecuteNonQueryAsync();
}
await using var reopened = CreateWriter();
var result = await reopened.ReadByExecutionIdsAsync([root], DateTime.MinValue, 2000);
Assert.Equal(row.EventId, Assert.Single(result).EventId);
// Index initialization remains idempotent on the upgraded database.
await using var reopenedAgain = CreateWriter();
Assert.Equal(row.EventId, Assert.Single(await reopenedAgain.ReadByExecutionIdsAsync([root], DateTime.MinValue, 2000)).EventId);
}
[Fact]
public async Task Cursor_PagesEqualTimestampsWithoutRepeatingOrSkippingRows()
{
var root = Guid.NewGuid();
await using var writer = CreateWriter();
var rows = Enumerable.Range(0, 7).Select(_ => Row(T0, root, null)).ToArray();
foreach (var row in rows) await writer.WriteAsync(row);
var first = await writer.ReadByExecutionIdsAsync([root], T0, 3);
// Reflection lets the pre-fix implementation demonstrate its repeated oldest page.
var method = typeof(SqliteAuditWriter).GetMethod(nameof(SqliteAuditWriter.ReadByExecutionIdsAsync))!;
object[] args = method.GetParameters().Length == 5
? [new[] { root }, T0, 3, CancellationToken.None, first[^1].EventId.ToString("D")]
: [new[] { root }, T0, 3, CancellationToken.None];
var second = await (Task<IReadOnlyList<AuditEvent>>)method.Invoke(writer, args)!;
Assert.Equal(6, first.Concat(second).Select(e => e.EventId).Distinct().Count());
Assert.Equal(rows.OrderBy(e => e.EventId.ToString("D"), StringComparer.Ordinal).Take(6).Select(e => e.EventId),
first.Concat(second).Select(e => e.EventId));
}
[Fact]
public async Task MatchesExecutionId_OrParentExecutionId_OrderedAndBounded()
{