using Microsoft.Data.Sqlite; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace ScadaLink.SiteEventLogging.Tests; /// /// Regression tests for SiteEventLogging-006: the schema must index the columns the /// query service filters on so common queries do not full-scan a 1 GB database. /// public class SchemaIndexTests : IDisposable { private readonly SiteEventLogger _logger; private readonly SqliteConnection _verifyConnection; private readonly string _dbPath; public SchemaIndexTests() { _dbPath = Path.Combine(Path.GetTempPath(), $"test_index_{Guid.NewGuid()}.db"); var options = Options.Create(new SiteEventLogOptions { DatabasePath = _dbPath }); _logger = new SiteEventLogger(options, NullLogger.Instance); _verifyConnection = new SqliteConnection($"Data Source={_dbPath}"); _verifyConnection.Open(); } public void Dispose() { _verifyConnection.Dispose(); _logger.Dispose(); if (File.Exists(_dbPath)) File.Delete(_dbPath); } [Fact] public void Schema_HasIndexOnSeverity() { using var cmd = _verifyConnection.CreateCommand(); cmd.CommandText = "SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = 'site_events'"; var indexes = new List(); using var reader = cmd.ExecuteReader(); while (reader.Read()) indexes.Add(reader.GetString(0)); Assert.Contains("idx_events_severity", indexes); } [Fact] public async Task SeverityFilteredQuery_UsesIndex_NotFullScan() { await _logger.LogEventAsync("script", "Error", null, "S", "boom"); await _logger.LogEventAsync("script", "Info", null, "S", "ok"); using var cmd = _verifyConnection.CreateCommand(); cmd.CommandText = "EXPLAIN QUERY PLAN SELECT id FROM site_events WHERE severity = 'Error'"; var plan = new System.Text.StringBuilder(); using var reader = cmd.ExecuteReader(); while (reader.Read()) { // The detail column holds the human-readable plan step. plan.Append(reader.GetString(reader.GetOrdinal("detail"))).Append('\n'); } var planText = plan.ToString(); Assert.Contains("idx_events_severity", planText); Assert.DoesNotContain("SCAN site_events", planText); } }