sqlinsight-pro/tests/SQLInsight.IntegrationTests/HubMaintenanceTests.cs
Claude 19fbb02045
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: data processing, hub retention and deployment
Hub: nightly retention of resolved findings, the submission log and
silent instances; delete-customer erases a customer's data and blocks
its licenses; persistent data protection keys and trusted reverse
proxies. Docker image and compose file with PostgreSQL. The agent
refuses to send the license to a non-https hub. Documentation for the
hub, for data processing (GDPR basis for a data processing agreement)
and the README roadmap for phase 3.

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

81 lines
4.1 KiB
C#

using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SQLInsight.Hub.Data;
using SQLInsight.Hub.Maintenance;
namespace SQLInsight.IntegrationTests;
[Trait("Category", "Integration")]
public class HubMaintenanceTests(HubFixture hub) : IClassFixture<HubFixture>
{
private async Task<HttpStatusCode> SubmitAsync(string license, params (string Rule, string Subject, string Severity)[] findings)
{
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/findings")
{
Content = JsonContent.Create(HubApiTests.Submission("sql01", DateTimeOffset.UtcNow, findings)),
};
request.Headers.Authorization = new AuthenticationHeaderValue("SQLInsight-License", license);
return (await hub.Factory.CreateClient().SendAsync(request)).StatusCode;
}
private async Task<T> WithDbAsync<T>(Func<HubDbContext, HubMaintenance, Task<T>> action)
{
await using var scope = hub.Factory.Services.CreateAsyncScope();
return await action(scope.ServiceProvider.GetRequiredService<HubDbContext>(), scope.ServiceProvider.GetRequiredService<HubMaintenance>());
}
[Fact]
public async Task Deleting_a_customer_removes_its_data_and_blocks_its_licenses()
{
var name = $"Slet {Guid.NewGuid():N}"[..20];
var license = hub.License(name);
Assert.Equal(HttpStatusCode.OK, await SubmitAsync(license, ("SQLI-DB-007", "database: Sales", "Critical")));
var deleted = await WithDbAsync((_, m) => m.DeleteCustomerAsync(name));
Assert.True(deleted);
var (customers, findings, instances, revoked) = await WithDbAsync(async (db, _) => (
await db.Customers.CountAsync(c => c.Name == name),
await db.Findings.CountAsync(f => f.Instance!.Customer!.Name == name),
await db.Instances.CountAsync(i => i.Customer!.Name == name),
await db.Licenses.Where(l => l.Customer!.Name.StartsWith("Slettet kunde #")).CountAsync(l => l.RevokedAt != null)));
Assert.Equal(0, customers);
Assert.Equal(0, findings);
Assert.Equal(0, instances);
Assert.True(revoked >= 1);
Assert.Equal(HttpStatusCode.Forbidden, await SubmitAsync(license, ("SQLI-DB-007", "database: Sales", "Critical")));
}
[Fact]
public async Task Retention_removes_old_resolved_findings_old_submissions_and_silent_instances()
{
var active = $"Aktiv {Guid.NewGuid():N}"[..20];
var silent = $"Tavs {Guid.NewGuid():N}"[..20];
await SubmitAsync(hub.License(active), ("SQLI-CFG-001", "", "High"), ("SQLI-DB-007", "database: A", "Critical"));
await SubmitAsync(hub.License(silent), ("SQLI-CFG-001", "", "High"));
await WithDbAsync(async (db, _) =>
{
var old = DateTime.UtcNow.AddDays(-400);
await db.Findings.Where(f => f.Instance!.Customer!.Name == active && f.RuleId == "SQLI-DB-007")
.ExecuteUpdateAsync(f => f.SetProperty(x => x.ResolvedAt, old));
await db.Submissions.Where(s => db.Instances.Any(i => i.Id == s.InstanceId && i.Customer!.Name == active))
.ExecuteUpdateAsync(s => s.SetProperty(x => x.ReceivedAt, old));
await db.Instances.Where(i => i.Customer!.Name == silent).ExecuteUpdateAsync(i => i.SetProperty(x => x.LastSubmissionAt, old));
return 0;
});
var result = await WithDbAsync((_, m) => m.ApplyRetentionAsync());
Assert.True(result.Findings >= 1 && result.Submissions >= 1 && result.Instances >= 1);
var (activeFindings, activeSubmissions, silentInstances) = await WithDbAsync(async (db, _) => (
await db.Findings.Where(f => f.Instance!.Customer!.Name == active).Select(f => f.RuleId).ToListAsync(),
await db.Submissions.CountAsync(s => db.Instances.Any(i => i.Id == s.InstanceId && i.Customer!.Name == active)),
await db.Instances.CountAsync(i => i.Customer!.Name == silent)));
Assert.Equal(["SQLI-CFG-001"], activeFindings);
Assert.Equal(0, activeSubmissions);
Assert.Equal(0, silentInstances);
}
}