After the rebase onto 1.4 the 16 SQLI-WIN/SQLI-SYN scripts had their own /* */ format, so SqlScriptTests (rule ids, review header, -- Kilde: blocks) failed. - The scripts have main's header (Regel-id, Titel, Alvor, Versioner, Vurdering, Grænseværdier, Rettigheder, Data kommer fra, Regelkode, Read-only); the Windows WQL/XPath stays word for word in comments. - The synthetic SQL check's SQL is registered (SyntheticCheckRunner.UseDatabaseSql, BeginTransactionSql, RollbackSql, SyntheticCheckSettings.DefaultQuery) and shown as -- Kilde: blocks in SQLI-SYN-001/002 and _collectors/SyntheticCheckRunner.sql. - SqlScriptTests know ServerRuleCatalog; only those rules may be without SQL blocks. - ServerScriptTests: every CimQueries query/filter is in a SQLI-WIN script word for word. - The SQL check cancels the rest of a large answer instead of draining it on close. - Rule counts: README (108 built-in: 92 SQL Server + 13 Windows + 3 synthetic), SQL Scripts/README (rule table, 124 rules, Windows/synthetic section), RuleCatalogTests, CONTRIBUTING (how to add a server rule). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
62 lines
3 KiB
C#
62 lines
3 KiB
C#
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace SQLInsight.Servers.Tests;
|
|
|
|
/// <summary>
|
|
/// The Windows queries in SQL Scripts/SQLI-WIN-*.sql (comment blocks, for review) are the ones the agent sends, word for
|
|
/// word: every query in <see cref="CimQueries"/> is shown in a script, and every script shows at least one of them.
|
|
/// </summary>
|
|
public sealed partial class ServerScriptTests
|
|
{
|
|
private static readonly DateTimeOffset Example = new(2026, 10, 2, 12, 0, 0, TimeSpan.Zero);
|
|
|
|
/// <summary>The query texts, the event log ones rendered as the scripts show them.</summary>
|
|
private static IEnumerable<(string Name, string Text)> Queries()
|
|
{
|
|
foreach (var field in typeof(CimQueries).GetFields(BindingFlags.Public | BindingFlags.Static)
|
|
.Where(f => f.IsLiteral && f.Name.EndsWith("Query", StringComparison.Ordinal)))
|
|
{
|
|
yield return ($"CimQueries.{field.Name}", (string)field.GetRawConstantValue()!);
|
|
}
|
|
|
|
yield return ("CimQueries.EventXPath", CimQueries.EventXPath(Example));
|
|
yield return ("CimQueries.EventQuery", CimQueries.EventQuery("System", Example));
|
|
yield return ("CimQueries.SingleServiceQuery", CimQueries.SingleServiceQuery("<tjeneste>"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Every_windows_query_is_shown_in_a_script_word_for_word()
|
|
{
|
|
var scripts = Scripts().ToDictionary(f => Path.GetFileName(f), f => Flatten(File.ReadAllText(f)));
|
|
var missing = Queries().Where(q => !scripts.Values.Any(s => s.Contains(Flatten(q.Text), StringComparison.Ordinal))).Select(q => q.Name).ToList();
|
|
var without = scripts.Where(s => !Queries().Any(q => s.Value.Contains(Flatten(q.Text), StringComparison.Ordinal))).Select(s => s.Key).ToList();
|
|
|
|
Assert.True(missing.Count == 0, "Står ikke ordret i SQL Scripts/SQLI-WIN-*.sql: " + string.Join(", ", missing));
|
|
Assert.True(without.Count == 0, "Viser ingen af agentens forespørgsler: " + string.Join(", ", without));
|
|
}
|
|
|
|
/// <summary>The comment text without the "--" prefixes, with all whitespace as single spaces (a query may wrap).</summary>
|
|
private static string Flatten(string text) =>
|
|
Whitespace().Replace(CommentPrefix().Replace(text.Replace("\r\n", "\n", StringComparison.Ordinal), " "), " ");
|
|
|
|
private static List<string> Scripts()
|
|
{
|
|
for (var directory = new DirectoryInfo(AppContext.BaseDirectory); directory is not null; directory = directory.Parent)
|
|
{
|
|
var folder = Path.Combine(directory.FullName, "SQL Scripts");
|
|
if (Directory.Exists(folder))
|
|
{
|
|
return Directory.EnumerateFiles(folder, "SQLI-WIN-*.sql").Order(StringComparer.Ordinal).ToList();
|
|
}
|
|
}
|
|
|
|
throw new DirectoryNotFoundException("SQL Scripts/ blev ikke fundet over testens mappe.");
|
|
}
|
|
|
|
[GeneratedRegex(@"^--", RegexOptions.Multiline)]
|
|
private static partial Regex CommentPrefix();
|
|
|
|
[GeneratedRegex(@"\s+")]
|
|
private static partial Regex Whitespace();
|
|
}
|