sqlinsight-pro/tests/SQLInsight.Agent.Tests/SubmissionBuilderTests.cs
Claude bfb27778d2
Some checks are pending
CI / Build og unit tests (push) Waiting to run
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
Fase 3: findings sync from agent to hub (queue, retry, data minimization)
- Evidence can be marked sensitive (query text, logins, hosts, programs,
  job error messages); rules mark it. Sensitive values and the server
  name are sent as [udeladt] unless the customer allows them.
- Full-state submission contract (all active findings and rule outcomes
  per instance), idempotent by submission id
- Local outbound queue in the repository (migration 0002): newest state
  per instance only, exponential backoff up to an hour, permanent
  rejections (bad license, bad request) are given up, sent items purged
- Hub status (license state, last sent, last error) for the web UI
- Sync runs only with a valid license (or in grace); monitoring never
  depends on it

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

87 lines
3.1 KiB
C#

using SQLInsight.Agent.Hub;
using SQLInsight.Contracts;
using SQLInsight.Core.Findings;
using SQLInsight.Core.Instances;
using SQLInsight.Repository;
using SQLInsight.Rules.Engine;
namespace SQLInsight.Agent.Tests;
public class SubmissionBuilderTests
{
private static readonly DateTimeOffset Now = new(2026, 9, 29, 12, 0, 0, TimeSpan.Zero);
private static readonly InstanceInfo Info = new()
{
ServerName = "SQL01.corp.local",
Version = SqlServerVersion.Parse("15.0.4490.9"),
ProductLevel = "RTM",
ProductUpdateLevel = "CU32",
Edition = "Enterprise Edition",
EngineEdition = EngineEdition.Enterprise,
};
private static readonly StoredFinding Blocking = new()
{
FindingId = 1,
RuleId = "SQLI-BLK-001",
Subject = "session: 51",
Severity = Severity.High,
Title = "Session 51 blokerer",
Explanation = "Forklaring",
Recommendation = "Anbefaling",
FirstSeenAt = Now.UtcDateTime.AddMinutes(-5),
Evidence =
[
new Evidence("Blokerede sessioner", "3"),
new Evidence("Head blockers SQL", "UPDATE dbo.Salaries SET Amount = 100000 WHERE Name = 'Hansen'", Sensitive: true),
],
};
private static RuleResult[] Results => [new RuleResult { RuleId = "SQLI-BLK-001", RuleTitle = "t", Outcome = RuleOutcome.Findings }];
[Fact]
public void Sensitive_evidence_and_server_name_stay_on_prem_by_default()
{
var submission = SubmissionBuilder.Build("prod-sql01", Info, Results, [Blocking], new HubOptions(), Now);
var evidence = Assert.Single(submission.Findings).Evidence;
Assert.Equal("3", evidence[0].Value);
Assert.Equal(FindingsSubmission.Redacted, evidence[1].Value);
Assert.Null(submission.Instance.ServerName);
Assert.Equal("prod-sql01", submission.Instance.InstanceKey);
Assert.Equal("SQL Server 2019", submission.Instance.Release);
}
[Fact]
public void Customer_can_allow_sensitive_evidence_and_server_name()
{
var options = new HubOptions { SendSensitiveEvidence = true, SendServerName = true };
var submission = SubmissionBuilder.Build("prod-sql01", Info, Results, [Blocking], options, Now);
Assert.StartsWith("UPDATE dbo.Salaries", submission.Findings[0].Evidence[1].Value, StringComparison.Ordinal);
Assert.Equal("SQL01.corp.local", submission.Instance.ServerName);
}
[Fact]
public void Every_submission_has_a_new_id_and_carries_rule_outcomes()
{
var a = SubmissionBuilder.Build("x", Info, Results, [], new HubOptions(), Now);
var b = SubmissionBuilder.Build("x", Info, Results, [], new HubOptions(), Now);
Assert.NotEqual(a.SubmissionId, b.SubmissionId);
Assert.Equal("Findings", Assert.Single(a.Rules).Outcome);
}
[Theory]
[InlineData(0, 1)]
[InlineData(1, 2)]
[InlineData(3, 8)]
[InlineData(10, 60)]
[InlineData(50, 60)]
public void Backoff_doubles_up_to_an_hour(int attempts, int minutes)
{
Assert.Equal(TimeSpan.FromMinutes(minutes), HubSyncService.Backoff(attempts));
}
}