Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.SiteEventLogging.Tests/SiteEventsSchemaCalibrationTests.cs
T
Joseph Doherty 513fa1aa16 docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)
Comment/XML-doc changes only:
- add summaries to ~10k test methods, fixtures, fakes and helpers
  (+ <param> on theories, <returns> on async Task methods,
  <inheritdoc/> on interface implementations the checker can resolve)
- strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N,
  #NN, Component-0NN) from comments, keeping the explanatory text;
  reword false positives in comments only
- real docs on fakes implementing BCL/library interfaces
  (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime)
- reword the last 5 src/ false positives (CWE-1236, failures-1,
  year-0001, M365)

Only non-comment edits: four one-line nested classes split across lines
so members can carry docs. Build 0 errors / 0 warnings.
2026-10-07 15:05:49 -04:00

111 lines
5.1 KiB
C#

using Microsoft.Data.Sqlite;
namespace ZB.MOM.WW.ScadaBridge.SiteEventLogging.Tests;
/// <summary>
/// Pins the <c>site_events</c> schema to the shape the storage-cap size model was calibrated
/// against (register row 93, <c>docs/plans/2026-08-17-site-event-log-cap-semantics-decision.md</c> §4).
/// </summary>
/// <remarks>
/// <see cref="EventLogPurgeService.EstimateSiteEventsSize"/> hardcodes this schema twice over:
/// the sampling SQL sums <c>octet_length</c> over the exact column list, and
/// <c>IndexedColumnCount = 4</c> plus the fill-factor/overhead constants are empirical for
/// exactly these four secondary indexes. A schema migration that adds a column or an index
/// invalidates both SILENTLY — the new column's bytes are never sampled, a fifth index is
/// never charged — so this test turns that drift into a loud failure. The fix on failure is
/// NEVER to just update the assertions: re-run the memo §4 calibration, update the
/// <see cref="EventLogPurgeService"/> constants and sampling SQL together, and only then
/// re-pin the schema here.
/// </remarks>
public class SiteEventsSchemaCalibrationTests
{
private const string RecalibrationNotice =
"site_events schema drifted from the size-model calibration. Re-run the calibration in " +
"docs/plans/2026-08-17-site-event-log-cap-semantics-decision.md §4 and update " +
"EventLogPurgeService's constants + sampling SQL together with this pin (register row 93).";
private static SqliteConnection OpenWithSchema()
{
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
SiteEventLogSchema.Apply(connection);
return connection;
}
private static List<string> ReadStrings(SqliteConnection connection, string sql)
{
using var cmd = connection.CreateCommand();
cmd.CommandText = sql;
var values = new List<string>();
using var reader = cmd.ExecuteReader();
while (reader.Read())
values.Add(reader.GetString(0));
return values;
}
/// <summary>Verifies that the modelled columns exactly match the sampled octet-length sum.</summary>
[Fact]
public void Columns_MatchTheSampledOctetLengthSum_Exactly()
{
// The sampler's payload_bytes expression sums octet_length over exactly these eight
// columns, in this order. A ninth column would contribute real disk bytes that the
// model never counts, under-sizing every estimate.
using var connection = OpenWithSchema();
var columns = ReadStrings(connection, "SELECT name FROM pragma_table_info('site_events')");
Assert.True(
columns.SequenceEqual(
["id", "timestamp", "event_type", "severity", "instance_id", "source", "message", "details"]),
$"{RecalibrationNotice} Actual columns: {string.Join(", ", columns)}");
}
/// <summary>Verifies that the table has exactly the four indexes the model charges for, plus the primary-key autoindex.</summary>
[Fact]
public void Indexes_AreExactlyTheFourTheModelCharges_PlusThePkAutoindex()
{
// IndexedColumnCount = 4 charges four secondary index entries per row; the TEXT
// primary key's autoindex is absorbed into the empirically-measured fill factors.
// A fifth secondary index would be silently under-counted.
using var connection = OpenWithSchema();
var indexes = ReadStrings(
connection, "SELECT name FROM pragma_index_list('site_events') ORDER BY name");
Assert.True(
indexes.SequenceEqual(
[
"idx_events_instance",
"idx_events_severity",
"idx_events_timestamp",
"idx_events_type",
"sqlite_autoindex_site_events_1",
]),
$"{RecalibrationNotice} Actual indexes: {string.Join(", ", indexes)}");
}
/// <summary>Verifies that each index covers exactly the column the sampler sums.</summary>
/// <param name="indexName">The index name.</param>
/// <param name="expectedColumn">The column the index is expected to cover.</param>
[Theory]
[InlineData("idx_events_timestamp", "timestamp")]
[InlineData("idx_events_type", "event_type")]
[InlineData("idx_events_instance", "instance_id")]
[InlineData("idx_events_severity", "severity")]
public void EachIndex_CoversExactlyTheColumnTheSamplerSums(string indexName, string expectedColumn)
{
// The sampler's indexed_bytes expression sums octet_length(timestamp) +
// octet_length(event_type) + octet_length(severity) + octet_length(instance_id) —
// one column per index. Widening an index to a composite (or re-pointing it) would
// change the real entry width without changing what is sampled.
using var connection = OpenWithSchema();
var columns = ReadStrings(
connection, $"SELECT name FROM pragma_index_info('{indexName}')");
Assert.True(
columns.SequenceEqual([expectedColumn]),
$"{RecalibrationNotice} Index {indexName} covers: {string.Join(", ", columns)}");
}
}