sqlinsight-pro/tests/SQLInsight.Rules.Tests/TestInstances.cs
Claude 63f23c4bbd
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Waiting to run
Fase 1: collectors and rule pack (41 rules)
Collectors (SQL Server 2000+, version-specific SQL, least privilege):
server, configuration, trace flags, databases (settings, backups in UTC,
CHECKDB, VLFs, Query Store, files, tempdb), suspect pages, plan cache,
wait stats and file I/O as deltas with restart/clear detection,
performance counters, CPU from the scheduler monitor ring buffer,
blocking (sleeping head blockers with open transactions), and deadlocks
from system_health continuing from the last file offset.

Rules: configuration (memory, MAXDOP per Microsoft NUMA guidance, cost
threshold, ad hoc bloat, risky options, pending changes), databases
(state, settings, backups, log backups, CHECKDB, suspect pages, VLFs,
autogrowth, max size, Query Store, tempdb), waits (THREADPOOL, signal
waits, locks, I/O, WRITELOG, parallelism, PAGELATCH), I/O latency, CPU,
memory grants, blocking and deadlocks. Missing data makes a rule not
measurable with the collector's reason.

Integration tests run every collector as the least-privilege login and
reproduce real blocking and a real deadlock; green on 2017 and 2022.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 06:17:08 +00:00

100 lines
3.7 KiB
C#

using SQLInsight.Core.Data;
using SQLInsight.Core.Instances;
using SQLInsight.Rules.Engine;
namespace SQLInsight.Rules.Tests;
internal static class TestInstances
{
public static readonly DateTimeOffset Now = new(2026, 9, 29, 12, 0, 0, TimeSpan.Zero);
public static RuleContext Context(string version, DateTimeOffset? now = null, EngineEdition edition = EngineEdition.Standard)
{
var info = new InstanceInfo
{
ServerName = "test",
Version = SqlServerVersion.Parse(version),
ProductLevel = "RTM",
Edition = "Standard Edition",
EngineEdition = edition,
};
return new RuleContext(info, InstanceCapabilities.From(info), now ?? Now);
}
/// <summary>SQL Server 2022 with the given data collected.</summary>
public static RuleContext With(params object[] data) => WithVersion("16.0.4135.4", data);
public static RuleContext WithVersion(string version, params object[] data)
{
var context = Context(version);
foreach (var item in data)
{
context.Data.Set(item.GetType(), item);
}
return context;
}
public static ConfigurationSnapshot Config(params (string Name, long Value)[] options) => new()
{
CapturedAt = Now,
Options = options.Select(o => new ConfigurationOption(o.Name, o.Value, o.Value)).ToList(),
};
public static ServerSnapshot Server(int cpus = 8, long memoryMb = 65536, int numa = 1) => new()
{
CapturedAt = Now,
SqlServerStartTime = Now.UtcDateTime.AddDays(-10),
LogicalCpuCount = cpus,
VisibleOnlineSchedulers = cpus,
NumaNodeCount = numa,
PhysicalMemoryMb = memoryMb,
};
public static DatabaseInfo Database(string name = "Sales", Func<DatabaseInfo, DatabaseInfo>? change = null)
{
var db = new DatabaseInfo
{
Name = name,
DatabaseId = 5,
State = "ONLINE",
RecoveryModel = "FULL",
CompatibilityLevel = 160,
PageVerify = "CHECKSUM",
CreateDate = Now.UtcDateTime.AddYears(-1),
LastFullBackup = Now.UtcDateTime.AddDays(-1),
LastLogBackup = Now.UtcDateTime.AddMinutes(-15),
LastGoodCheckDb = Now.UtcDateTime.AddDays(-2),
VirtualLogFileCount = 50,
};
return change is null ? db : change(db);
}
public static DatabaseSnapshot DatabaseList(params DatabaseInfo[] databases) => new() { CapturedAt = Now, Databases = databases };
public static CumulativeSample<WaitStatsSnapshot, WaitStatsDelta> Waits(TimeSpan interval, params WaitStat[] waits)
{
var snapshot = new WaitStatsSnapshot { CapturedAt = Now, SqlServerStartTime = Now.UtcDateTime.AddDays(-1), Waits = waits };
return new(snapshot, new WaitStatsDelta { From = Now - interval, To = Now, Waits = waits });
}
public static CumulativeSample<FileIoSnapshot, FileIoDelta> Io(params FileIoStat[] files)
{
var snapshot = new FileIoSnapshot { CapturedAt = Now, SqlServerStartTime = Now.UtcDateTime.AddDays(-1), Files = files };
return new(snapshot, new FileIoDelta { From = Now.AddMinutes(-5), To = Now, Files = files });
}
public static FileIoStat File(string type, long reads = 0, long readStallMs = 0, long writes = 0, long writeStallMs = 0) => new()
{
DatabaseName = "Sales",
FileId = type == "LOG" ? 2 : 1,
FileType = type,
PhysicalName = type == "LOG" ? "L:\\Sales_log.ldf" : "D:\\Sales.mdf",
Reads = reads,
BytesRead = reads * 8192,
ReadStallMs = readStallMs,
Writes = writes,
BytesWritten = writes * 8192,
WriteStallMs = writeStallMs,
};
}